public inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
50+ messages / 2 participants
[nested] [flat]

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1473 ++++++++++++++++++++++++-------------------------
 1 file changed, 722 insertions(+), 751 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index d1e915c11a..2cd75a9673 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,123 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     <productname>PostgreSQL</productname> offers built-in support for the
+     following forms of partitioning:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+
+     If your application needs to use other forms of partitioning not listed
+     above, alternative methods such as inheritance and
+     <literal>UNION ALL</literal> views can be used instead.  Such methods
+     offer flexibility but do not have some of the performance benefits
+     of built-in declarative partitioning.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,74 +2899,72 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
-    Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    Partitions may themselves be defined as partitioned tables, referred to as
+    <firstterm>sub-partitioning</firstterm>.  Partitions may have their own
+    indexes, constraints and default values, distinct from other partitions.
+    They do not inherit indexes from the partitioned table.  See
+    <xref linkend="sql-createtable"> for more details on creating partitioned
+    tables and partitions.
    </para>
 
    <para>
-    Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
-    <xref linkend="sql-altertable"> to learn more about the
-    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
+    It is not possible to turn a regular table into a partitioned table or
+    vice versa.  However, it is possible to add a regular or partitioned table
+    containing data as a partition of a partitioned table, or remove a
+    partition from a partitioned table turning it into a standalone table;
+    see <xref linkend="sql-altertable"> to learn more about the
+    <command>ATTACH PARTITION</> and <command>DETACH PARTITION</>
+    sub-commands.
    </para>
 
    <para>
     Individual partitions are linked to the partitioned table with inheritance
-    behind-the-scenes, however it is not possible to use some of the inheritance
-    features discussed in the previous section with partitioned tables and
-    partitions.  For example, partitions cannot have any other parents than
-    the partitioned table it is a partition of, nor can a regular table inherit
-    from a partitioned table making the latter its parent.  That means
-    partitioned table and partitions do not participate in inheritance with
-    regular tables.  Since a partition hierarchy consisting of the
-    partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    behind-the-scenes, however it is not possible to use some of the
+    inheritance features discussed in the previous section with partitioned
+    tables and partitions.  For example, a partition cannot have any parents
+    other than the partitioned table it is a partition of, nor can a regular
+    table inherit from a partitioned table making the latter its parent.
+    That means partitioned table and partitions do not participate in
+    inheritance with regular tables.  Since a partition hierarchy consisting
+    of the partitioned table and its partitions is still an inheritance
+    hierarchy, all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
       <para>
        Both <literal>CHECK</literal> and <literal>NOT NULL</literal>
        constraints of a partitioned table are always inherited by all its
-       partitions.  There cannot be any <literal>CHECK</literal> constraints
-       that are marked <literal>NO INHERIT</literal>.
+       partitions.  <literal>CHECK</literal> constraints that are marked
+       <literal>NO INHERIT</literal> are not allowed.
       </para>
      </listitem>
 
      <listitem>
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
-       would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       will cause an error for partitioned tables in the case of
+       schema-modifying commands such as most <literal>ALTER TABLE</literal>
+       commands.  For example, dropping a column from only the parent does
+       not make sense for partitioned tables.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Partitions cannot have columns that are not present in the parent.
-       It is neither possible to specify columns when creating partitions
-       with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       Partitions cannot have columns that are not present in the parent.  It
+       is neither possible to specify columns when creating partitions with
+       <command>CREATE TABLE</> nor is it possible to add columns to
+       partitions after-the-fact using <command>ALTER TABLE</>.  Tables may be
+       added as a partition with <command>ALTER TABLE ... ATTACH PARTITION</>
+       only if their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,487 +2978,505 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
-    although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
+    although certain limitations exist in their usage.  For example, data
+    inserted into the partitioned table is not routed to foreign table
     partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> in this
+       case) and the list of column(s) to use as the partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       You may decide to use multiple columns in the partition key for range
+       partitioning if it's known that each of the selected columns will
+       divide the incoming data using successively more granular partition
+       criteria.  Whereas using fewer columns may lead to coarser-grained
+       partitioning causing each partition to accept bigger set of data than
+       might be desirable.  A query accessing the partitioned table will have
+       to scan fewer partitions if the conditions involve some or all of these
+       columns.  For example, consider a table range partitioned using columns
+       <structfield>lastname</> and <structfield>firstname</> (in that order)
+       as the partition key.
       </para>
-     </listitem>
 
-     <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into the parent table that does not map
+       to one of the existing partitions will cause an error; appropriate
+       partition must be added manually.
       </para>
-     </listitem>
-    </itemizedlist>
-   </para>
-
-   <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
-   </para>
-   
-  </sect1>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
-
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible to specify
+       tablespace, storage parameters for each partition separately.
+      </para>
 
-   <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
 
-   <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
+      </para>
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+      <para>
+       To implement sub-partitioning, specify the
+       <literal>PARTITION BY</literal> clause in the commands used to create
+       individual partitions, for example:
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+       After creating partitions of <structname>measurement_y2006m02</>,
+       any data inserted into <structname>measurement</> that is mapped to
+       <structname>measurement_y2006m02</> (or data that is directly inserted
+       into <structname>measurement_y2006m02</>, provided it satisfies its
+       partition constraint) will be further redirected to one of its
+       partitions based on the <structfield>peaktemp</> column.  Partition
+       key specified may overlap with the parent's partition key, although
+       care must be taken when specifying the bounds of a sub-partition
+       such that the set of data it accepts constitutes a subset of what
+       the partition's own bounds allows; the system does not try to check
+       if that's really the case.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       Create an index on the key column(s), as well as any other indexes you
+       might want for every partition.
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
+      </para>
+     </listitem>
 
       <listitem>
        <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
-     </varlistentry>
-    </variablelist>
+    </orderedlist>
    </para>
 
    <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
-
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
-
-     <varlistentry>
-      <term>List Partitioning</term>
-
-      <listitem>
-       <para>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   </sect2>
+   </sect3>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <quote>master</quote> table, from which all of the
-        partitions will inherit.
-       </para>
-       <para>
-        This table will contain no data.  Do not define any check
-        constraints on this table, unless you intend them to
-        be applied equally to all partitions.  There is no point
-        in defining any indexes or unique constraints on it, either.
-       </para>
-      </listitem>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-      <listitem>
-       <para>
-        Create several <quote>child</quote> tables that each inherit from
-        the master table.  Normally, these tables will not add any columns
-        to the set inherited from the master.
-       </para>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
-       <para>
-        We will refer to the child tables as partitions, though they
-        are in every way normal <productname>PostgreSQL</> tables
-        (or, possibly, foreign tables).
-       </para>
-      </listitem>
+   <para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-      <listitem>
-       <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
-       </para>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
+
+   <para>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-       <para>
-        Typical examples would be:
 <programlisting>
-CHECK ( x = 1 )
-CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
-CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
 </programlisting>
-        Ensure that the constraints guarantee that there is no overlap
-        between the key values permitted in different partitions.  A common
-        mistake is to set up range constraints like:
+
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
+
 <programlisting>
-CHECK ( outletID BETWEEN 100 AND 200 )
-CHECK ( outletID BETWEEN 200 AND 300 )
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
+
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
+
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
-        This is wrong since it is not clear which partition the key value
-        200 belongs in.
-       </para>
+    </para>
 
-       <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
+    <para>
+     Before running the <command>ATTACH PARTITION</> command, it is
+     recommended to create a <literal>CHECK</> constraint on the table to
+     be attached describing the desired partition constraint.  Using the
+     same, system is able to skip the scan to validate the implicit
+     partition constraint. Without such a constraint, the table will be
+     scanned to validate the partition constraint while holding an
+     <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+     One may then drop the constraint after <command>ATTACH PARTITION</>
+     is finished, because it is no longer necessary.
+    </para>
+   </sect3>
 
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
+   <para>
+    The following limitations apply to partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.  This also means that there is no way to create a primary
+       key, unique constraint, or exclusion constraint spanning all
+       partitions; it is only possible to constrain each leaf partition
+       individually.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables, foreign
+       keys referencing partitioned tables are not supported, nor are foreign
+       key references from a partitioned table to some other table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Using the <literal>ON CONFLICT</literal> clause with partitioned tables
+       will cause an error if <literal>DO UPDATE</literal> is specified as the
+       alternative action, because unique or exclusion constraints can only be
+       created on individual partitions.  There is no support for enforcing
+       uniqueness (or an exclusion constraint) across an entire partitioning
+       hierarchy.
+      </para>
+     </listitem>
+
+     <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.
+      </para>
+     </listitem>
 
-     </orderedlist>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions,
+       not the partitioned table as it is not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
     </para>
+    </sect3>
+   </sect2>
 
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
     <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
+     While the built-in declarative partitioning is suitable for most
+     common use cases, there are some circumstances where a more flexible
+     approach may be useful.  Partitioning can be implemented using table
+     inheritance, which allows for several features which are not supported
+     by declarative partitioning, such as:
+
+     <itemizedlist>
       <listitem>
        <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
+        Partitioning enforces a rule that all partitions must have exactly
+        the same set of columns as the parent, but table inheritance allows
+        children to have extra columns not present in the parent.
        </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
       </listitem>
 
       <listitem>
        <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
+        Table inheritance allows for multiple inheritance.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
+        Declarative partitioning only supports list and range partitioning,
+        whereas table inheritance allows data to be divided in a manner of
+        the user's choosing.  (Note, however, that if constraint exclusion is
+        unable to prune partitions effectively, query performance will be very
+        poor.)
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
+        Some operations require a stronger lock when using declarative
+        partitioning than when using table inheritance.  For example, adding
+        or removing a partition to or from a partitioned table requires taking
+        an <literal>ACCESS EXCLUSIVE</literal> lock on the parent table,
+        whereas a <literal>SHARE UPDATE EXCLUSIVE</literal> lock is enough
+        in the case of regular inheritance.
        </para>
       </listitem>
-
-     </orderedlist>
+     </itemizedlist>
     </para>
 
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
+     <para>
+      We use the same <structname>measurement</structname> table we used
+      above.  To implement it as a partitioned table using inheritance, use
+      the following steps:
 
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
+      <orderedlist spacing="compact">
+       <listitem>
+        <para>
+         Create the <quote>master</quote> table, from which all of the
+         partitions will inherit.  This table will contain no data.  Do not
+         define any check constraints on this table, unless you intend them
+         to be applied equally to all partitions.  There is no point in
+         defining any indexes or unique constraints on it, either.  For our
+         example, master table is the <structname>measurement</structname>
+         table as originally defined.
+        </para>
+       </listitem>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
+       <listitem>
+        <para>
+         Create several <quote>child</quote> tables that each inherit from
+         the master table.  Normally, these tables will not add any columns
+         to the set inherited from the master.
+        </para>
 
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
+        <para>
+         We will refer to the child tables as partitions, though they are
+         in every way normal <productname>PostgreSQL</> tables (or, possibly,
+         foreign tables).
+        </para>
 
+        <para>
+         This solves one of our problems: deleting old data. Each
+         month, all we will need to do is perform a <command>DROP
+         TABLE</command> on the oldest child table and create a new
+         child table for the new month's data.
 <programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
 ...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
 </programlisting>
+        </para>
+       </listitem>
 
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
+       <listitem>
+        <para>
+         Add non-overlapping table constraints to the partition tables to
+         define the allowed key values in each partition.
+        </para>
 
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
+        <para>
+         Typical examples would be:
+<programlisting>
+CHECK ( x = 1 )
+CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
+CHECK ( outletID &gt;= 100 AND outletID &lt; 200 )
+</programlisting>
+         Ensure that the constraints guarantee that there is no overlap
+         between the key values permitted in different partitions.  A common
+         mistake is to set up range constraints like:
+<programlisting>
+CHECK ( outletID BETWEEN 100 AND 200 )
+CHECK ( outletID BETWEEN 200 AND 300 )
+</programlisting>
+         This is wrong since it is not clear which partition the key value
+         200 belongs in.
+        </para>
 
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        <para>
+         It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
-       </para>
-      </listitem>
+        </para>
 
-      <listitem>
-       <para>
-        We probably need indexes on the key columns too:
+        <para>
+         Note that there is no difference in syntax between range and list
+         partitioning; those terms are descriptive only.
+        </para>
+       </listitem>
 
+       <listitem>
+        <para>
+         For each partition, create an index on the key column(s),
+         as well as any other indexes you might want.  (The key index is
+         not strictly necessary, but in most scenarios it is helpful.
+         If you intend the key values to be unique then you should
+         always create a unique or primary-key constraint for each
+         partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
+        </para>
+       </listitem>
 
-        We choose not to add further indexes at this time.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We want our application to be able to say <literal>INSERT INTO
-        measurement ...</> and have the data be redirected into the
-        appropriate partition table.  We can arrange that by attaching
-        a suitable trigger function to the master table.
-        If data will be added only to the latest partition, we can
-        use a very simple trigger function:
+       <listitem>
+        <para>
+         We want our application to be able to say <literal>INSERT INTO
+         measurement ...</> and have the data be redirected into the
+         appropriate partition table.  We can arrange that by attaching
+         a suitable trigger function to the master table.
+         If data will be added only to the latest partition, we can
+         use a very simple trigger function:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3363,9 +3488,11 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+        </para>
 
-        After creating the function, we create a trigger which
-        calls the trigger function:
+        <para>
+         After creating the function, we create a trigger which
+         calls the trigger function:
 
 <programlisting>
 CREATE TRIGGER insert_measurement_trigger
@@ -3373,15 +3500,15 @@ CREATE TRIGGER insert_measurement_trigger
     FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger();
 </programlisting>
 
-        We must redefine the trigger function each month so that it always
-        points to the current partition.  The trigger definition does
-        not need to be updated, however.
-       </para>
+         We must redefine the trigger function each month so that it always
+         points to the current partition.  The trigger definition does
+         not need to be updated, however.
+        </para>
 
-       <para>
-        We might want to insert data and have the server automatically
-        locate the partition into which the row should be added. We
-        could do this with a more complex trigger function, for example:
+        <para>
+         We might want to insert data and have the server automatically
+         locate the partition into which the row should be added. We
+         could do this with a more complex trigger function, for example:
 
 <programlisting>
 CREATE OR REPLACE FUNCTION measurement_insert_trigger()
@@ -3393,183 +3520,120 @@ BEGIN
     ELSIF ( NEW.logdate &gt;= DATE '2006-03-01' AND
             NEW.logdate &lt; DATE '2006-04-01' ) THEN
         INSERT INTO measurement_y2006m03 VALUES (NEW.*);
-    ...
-    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
-            NEW.logdate &lt; DATE '2008-02-01' ) THEN
-        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-    ELSE
-        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
-    END IF;
-    RETURN NULL;
-END;
-$$
-LANGUAGE plpgsql;
-</programlisting>
-
-        The trigger definition is the same as before.
-        Note that each <literal>IF</literal> test must exactly match the
-        <literal>CHECK</literal> constraint for its partition.
-       </para>
-
-       <para>
-        While this function is more complex than the single-month case,
-        it doesn't need to be updated as often, since branches can be
-        added in advance of being needed.
-       </para>
-
-       <note>
-        <para>
-         In practice it might be best to check the newest partition first,
-         if most inserts go into that partition.  For simplicity we have
-         shown the trigger's tests in the same order as in other parts
-         of this example.
-        </para>
-       </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create the <structname>measurement</> table as a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+    ...
+    ELSIF ( NEW.logdate &gt;= DATE '2008-01-01' AND
+            NEW.logdate &lt; DATE '2008-02-01' ) THEN
+        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
+    ELSE
+        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
+    END IF;
+    RETURN NULL;
+END;
+$$
+LANGUAGE plpgsql;
 </programlisting>
-       </para>
-      </listitem>
 
-      <listitem>
-       <para>
-        Then create partitions as follows:
+         The trigger definition is the same as before.
+         Note that each <literal>IF</literal> test must exactly match the
+         <literal>CHECK</literal> constraint for its partition.
+        </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
-       </para>
-      </listitem>
+        <para>
+         While this function is more complex than the single-month case,
+         it doesn't need to be updated as often, since branches can be
+         added in advance of being needed.
+        </para>
 
-      <listitem>
-       <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
-       </para>
-      </listitem>
-     </orderedlist>
+        <note>
+         <para>
+          In practice it might be best to check the newest partition first,
+          if most inserts go into that partition.  For simplicity we have
+          shown the trigger's tests in the same order as in other parts
+          of this example.
+         </para>
+        </note>
 
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
+        <para>
+         A different approach to redirecting inserts into the appropriate
+         partition table is to set up rules, instead of a trigger, on the
+         master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
 
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
-    </para>
-
-    <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
+         A rule has significantly more overhead than a trigger, but the
+         overhead is paid once per query rather than once per row, so this
+         method might be advantageous for bulk-insert situations.  In most
+         cases, however, the trigger method will offer better performance.
+        </para>
 
-   </sect2>
+        <para>
+         Be aware that <command>COPY</> ignores rules.  If you want to
+         use <command>COPY</> to insert data, you'll need to copy into the
+         correct partition table rather than into the master. <command>COPY</>
+         does fire triggers, so you can use it normally if you use the trigger
+         approach.
+        </para>
 
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
+        <para>
+         Another disadvantage of the rule approach is that there is no simple
+         way to force an error if the set of rules doesn't cover the insertion
+         date; the data will silently go into the master table instead.
+        </para>
+       </listitem>
 
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
+       <listitem>
+        <para>
+         Ensure that the <xref linkend="guc-constraint-exclusion">
+         configuration parameter is not disabled in
+         <filename>postgresql.conf</>.
+         If it is, queries will not be optimized as desired.
+        </para>
+       </listitem>
+      </orderedlist>
+     </para>
 
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     <para>
+      As we can see, a complex partitioning scheme could require a
+      substantial amount of DDL.  In the above example we would be creating
+      a new partition each month, so it might be wise to write a script that
+      generates the required DDL automatically.
+     </para>
+    </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+    <sect3 id="ddl-partitioning-inheritance-maintenance">
+     <title>Partition Maintenance</title>
+     <para>
+      To remove old data quickly, simply to drop the partition that is no
+      longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
-
-   <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
+     </para>
 
-     When using a partitioned table:
+    <para>
+     To remove the partition from the partitioned table but retain access to
+     it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+    </para>
 
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
-   </para>
-
-   <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+    <para>
+     To add a new partition to handle new data, create an empty partition
+     just as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,17 +3641,9 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may want to create the new table outside the partition
+     structure, and make it a partition after the data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
@@ -3598,31 +3654,64 @@ ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
 
-     The last of the above commands when using a partitioned table would be:
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
 
+    <para>
+     The following caveats apply to partitioned tables implemented using
+     inheritance:
+     <itemizedlist>
+      <listitem>
+       <para>
+        There is no automatic way to verify that all of the
+        <literal>CHECK</literal> constraints are mutually
+        exclusive.  It is safer to create code that generates
+        partitions and creates and/or modifies associated objects than
+        to write each by hand.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        The schemes shown here assume that the partition key column(s)
+        of a row never change, or at least do not change enough to require
+        it to move to another partition.  An <command>UPDATE</> that attempts
+        to do that will fail because of the <literal>CHECK</> constraints.
+        If you need to handle such cases, you can put suitable update triggers
+        on the partition tables, but it makes management of the structure
+        much more complicated.
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        If you are using manual <command>VACUUM</command> or
+        <command>ANALYZE</command> commands, don't forget that
+        you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+        will only process the master table.
+       </para>
+      </listitem>
 
-    <tip>
-     <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
-     </para>
-    </tip>
-   </sect2>
+      <listitem>
+       <para>
+        <command>INSERT</command> statements with <literal>ON CONFLICT</>
+        clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+        action is only taken in case of unique violations on the specified
+        target relation, not its child relations.
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3721,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,160 +3805,15 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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>
-      Using the <literal>ON CONFLICT</literal> clause with partitioned tables
-      will cause an error if <literal>DO UPDATE</literal> is specified as the
-      alternative action, because unique or exclusion constraints can only be
-      created on individual partitions.  There is no support for enforcing
-      uniqueness (or an exclusion constraint) across an entire partitioning
-      hierarchy.
-     </para>
-    </listitem>
-
-   </itemizedlist>
+    Constraint exclusion is also used for declarative partitioning, however
+    it is not required to create <literal>CHECK</literal> constraints for
+    individual partitions as when using table inheritance.
    </para>
 
    <para>
-    The following caveats apply to constraint exclusion, which is currently
-    used by both inheritance and partitioned tables:
+    The following caveats apply to constraint exclusion, which is used by
+    both inheritance and partitioned tables:
 
    <itemizedlist>
     <listitem>
@@ -3909,6 +3854,32 @@ ANALYZE measurement;
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+  </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------4AB4E704D39F2BF05E79CD67
Content-Type: text/plain
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
MIME-Version: 1.0


-- 
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

--------------4AB4E704D39F2BF05E79CD67--






^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------19C79A137EF2D0DB3BE72F79
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning
@ 2017-03-03 07:39 amit <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: amit @ 2017-03-03 07:39 UTC (permalink / raw)

Merge sections Partitioned Tables and Partitioning into one section
called Table Partitioning and Related Solutions.
---
 doc/src/sgml/ddl.sgml | 1359 +++++++++++++++++++++++++------------------------
 1 file changed, 707 insertions(+), 652 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 09b5b3ff70..a2dd39df54 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -2772,14 +2772,181 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </sect2>
   </sect1>
 
-  <sect1 id="ddl-partitioned-tables">
-   <title>Partitioned Tables</title>
+  <sect1 id="ddl-partitioning">
+   <title>Table Partitioning and Related Solutions</title>
+
+   <indexterm>
+    <primary>partitioning</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>table</primary>
+    <secondary>partitioning</secondary>
+   </indexterm>
 
    <indexterm>
     <primary>partitioned table</primary>
    </indexterm>
 
    <para>
+    <productname>PostgreSQL</productname> supports basic table
+    partitioning. This section describes why and how to implement
+    partitioning as part of your database design.
+   </para>
+
+   <sect2 id="ddl-partitioning-overview">
+     <title>Overview</title>
+
+    <para>
+     Partitioning refers to splitting what is logically one large table into
+     smaller physical pieces.  Partitioning can provide several benefits:
+    <itemizedlist>
+     <listitem>
+      <para>
+       Query performance can be improved dramatically in certain situations,
+       particularly when most of the heavily accessed rows of the table are in a
+       single partition or a small number of partitions.  The partitioning
+       substitutes for leading columns of indexes, reducing index size and
+       making it more likely that the heavily-used parts of the indexes
+       fit in memory.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       When queries or updates access a large percentage of a single
+       partition, performance can be improved by taking advantage
+       of sequential scan of that partition instead of using an
+       index and random access reads scattered across the whole table.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Bulk loads and deletes can be accomplished by adding or removing
+       partitions, if that requirement is planned into the partitioning design.
+       Doing <command>ALTER TABLE DETACH PARTITION</> followed by
+       <command>DROP TABLE</> is far faster than a bulk operation.  These
+       commands also entirely avoid the <command>VACUUM</command> overhead
+       caused by a bulk <command>DELETE</>.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       Seldom-used data can be migrated to cheaper and slower storage media.
+      </para>
+     </listitem>
+    </itemizedlist>
+
+     The benefits will normally be worthwhile only when a table would
+     otherwise be very large. The exact point at which a table will
+     benefit from partitioning depends on the application, although a
+     rule of thumb is that the size of the table should exceed the physical
+     memory of the database server.
+    </para>
+
+    <para>
+     The following forms of partitioning can be implemented in
+     <productname>PostgreSQL</productname>:
+
+     <variablelist>
+      <varlistentry>
+       <term>Range Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned into <quote>ranges</quote> defined
+         by a key column or set of columns, with no overlap between
+         the ranges of values assigned to different partitions.  For
+         example one might partition by date ranges, or by ranges of
+         identifiers for particular business objects.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>List Partitioning</term>
+
+       <listitem>
+        <para>
+         The table is partitioned by explicitly listing which key values
+         appear in each partition.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     The following partitioning methods are currently supported:
+
+     <variablelist>
+      <varlistentry>
+       <term>Declarative Partitioning</term>
+
+       <listitem>
+        <para>
+         One creates a <firstterm>partitioned table</firstterm> by specifying
+         the partitioning method and a set of columns as the partition key.
+         <firstterm>Partitions</firstterm>, which contain actual data inserted
+         into the table, are created by specifying what subset of the data it
+         accepts.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using inheritance</term>
+
+       <listitem>
+        <para>
+         Each partition must be created as a child table of a single parent
+         table.  The parent table itself is normally empty; it exists just to
+         represent the entire data set.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Using UNION ALL views</term>
+
+       <listitem>
+        <para>
+         One can define a <literal>UNION ALL</literal> view over
+         <literal>SELECT</literal> on individual tables, each of which
+         contains a partition of data.  Partitions are added or removed
+         by updating the view definition.
+        </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Accessing Tables using BRIN Indexes</term>
+
+       <listitem>
+        <para>
+         <acronym>BRIN</acronym>, which stands for Block Range Index is,
+         designed for handling very large tables in which certain columns
+         have some natural physical location within the table.  Scanning
+         a large table using a <acronym>BRIN</acronym> index results in
+         reading only a portion of the table, which is often why partitioning
+         is implemented.
+        </para>
+       </listitem>
+      </varlistentry>
+     </variablelist>
+    </para>
+
+    <para>
+     Each of the above mentioned methods is described below.
+    </para>
+   </sect2>
+
+  <sect2 id="ddl-partitioning-declarative">
+   <title>Declarative Partitioning</title>
+
+   <para>
     PostgreSQL offers a way to specify how to divide a table into pieces
     called partitions.  The table that is divided is referred to as a
     <firstterm>partitioned table</firstterm>.  The specification consists
@@ -2790,25 +2957,29 @@ VALUES ('Albany', NULL, NULL, 'NY');
    <para>
     All rows inserted into a partitioned table will be routed to one of the
     <firstterm>partitions</firstterm> based on the value of the partition
-    key.  Each partition has a subset defined by its <firstterm>partition
-    bounds</firstterm>.  Currently supported partitioning methods include
-    range and list, wherein each partition is assigned a range of keys or
-    a list of keys, respectively.
+    key.  Each partition has a subset of the data defined by its
+    <firstterm>partition bounds</firstterm>.  Currently supported
+    partitioning methods include range and list, where each partition is
+    assigned a range of keys and a list of keys, respectively.
    </para>
 
    <para>
     Partitions may have their own indexes, constraints and default values,
-    distinct from other partitions. Partitions do not inherit indexes from
-    the partitioned table.
+    distinct from other partitions. Partitions do not currently inherit
+    indexes from the partitioned table.
+   </para>
+
+   <para>
+    See <xref linkend="sql-createtable"> for more details creating partitioned
+    tables and partitions.
    </para>
 
    <para>
     Partitions may themselves be defined as partitioned tables, referred to as
-    <firstterm>sub-partitioning</firstterm>.  See <xref linkend="sql-createtable">
-    for more details creating partitioned tables and partitions.  It is not
-    currently possible to alter a regular table into a partitioned table or
-    vice versa.  However, it is possible to add a regular table containing
-    data into a partition of a partitioned table, or remove a partition; see
+    <firstterm>sub-partitioning</firstterm>.  It is not currently possible to
+    alter a regular table into a partitioned table or vice versa.  However,
+    it is possible to add a regular or partitioned table containing data into
+    a partition of a partitioned table, or remove a partition; see
     <xref linkend="sql-altertable"> to learn more about the
     <command>ATTACH PARTITION</> and <command>DETACH PARTITION</> sub-commands.
    </para>
@@ -2823,8 +2994,8 @@ VALUES ('Albany', NULL, NULL, 'NY');
     partitioned table and partitions do not participate in inheritance with
     regular tables.  Since a partition hierarchy consisting of the
     partitioned table and its partitions is still an inheritance hierarchy,
-    all the normal rules of inheritance apply as described in the previous
-    section (<xref linkend="ddl-inherit">) with some exceptions, most notably:
+    all the normal rules of inheritance apply as described in
+    <xref linkend="ddl-inherit"> with some exceptions, most notably:
 
     <itemizedlist>
      <listitem>
@@ -2840,13 +3011,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
       <para>
        The <literal>ONLY</literal> notation used to exclude child tables
        would either cause error or will be ignored in some cases for
-       partitioned tables.  For example, specifying <literal>ONLY</literal>
-       when querying data from a partitioned table would not make much sense,
-       because all the data is contained in partitions, so this raises an
-       error.  Specifying <literal>ONLY</literal> when modifying schema is
-       not desirable in certain cases with partitioned tables where it may be
-       fine for regular inheritance parents (for example, dropping a column
-       from only the parent); an error will be thrown in that case.
+       partitioned tables.  Specifying <literal>ONLY</literal> when modifying
+       schema is not desirable in certain cases with partitioned tables
+       whereas it may be fine for regular inheritance parents (for example,
+       dropping a column from only the parent); an error will be thrown in
+       that case.
       </para>
      </listitem>
 
@@ -2855,9 +3024,9 @@ VALUES ('Albany', NULL, NULL, 'NY');
        Partitions cannot have columns that are not present in the parent.
        It is neither possible to specify columns when creating partitions
        with <command>CREATE TABLE</> nor is it possible to add columns to
-       partitions using <command>ALTER TABLE</>. Tables may be added with
-       <command>ALTER TABLE ... ATTACH PARTITION</> if their columns exactly
-       match the parent, including oids.
+       partitions using <command>ALTER TABLE</>.  Tables may be added as a
+       partition with <command>ALTER TABLE ... ATTACH PARTITION</> only if
+       their columns exactly match the parent, including oids.
       </para>
      </listitem>
 
@@ -2871,199 +3040,353 @@ VALUES ('Albany', NULL, NULL, 'NY');
    </para>
 
    <para>
-    Partitions can also be foreign tables (see <xref linkend="ddl-foreign-data">),
+    Partitions can also be foreign tables (see <xref linkend="sql-createforeigntable">),
     although certain limitations exist currently in their usage.  For example,
-    data inserted into the partitioned table cannot be routed to foreign table
-    partitions.
+    data inserted into the partitioned table is currently not routed to foreign
+    table partitions.
    </para>
 
+   <sect3 id="ddl-partitioning-declarative-example">
+    <title>Example</title>
+
    <para>
-    There are currently the following limitations of using partitioned tables:
-    <itemizedlist>
+    Suppose we are constructing a database for a large ice cream company.
+    The company measures peak temperatures every day as well as ice cream
+    sales in each region. Conceptually, we want a table like:
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+);
+</programlisting>
+
+    We know that most queries will access just the last week's, month's or
+    quarter's data, since the main use of this table will be to prepare
+    online reports for management.  To reduce the amount of old data that
+    needs to be stored, we decide to only keep the most recent 3 years
+    worth of data. At the beginning of each month we will remove the oldest
+    month's data.  In this situation we can use partitioning to help us meet
+    all of our different requirements for the measurements table.
+   </para>
+
+   <para>
+    To use declarative partitioning in this case, use the following steps:
+
+    <orderedlist spacing="compact">
      <listitem>
       <para>
-       It is currently not possible to add same set of indexes on all partitions
-       automatically. Indexes must be added to each partition with separate
-       commands.
+       Create <structname>measurement</structname> table as a partitioned
+       table by specifying the <literal>PARTITION BY</literal> clause, which
+       includes the partitioning method (<literal>RANGE</literal> or
+       <literal>LIST</literal>) and the list of column(s) to use as the
+       partition key.
+
+<programlisting>
+CREATE TABLE measurement (
+    city_id         int not null,
+    logdate         date not null,
+    peaktemp        int,
+    unitsales       int
+) PARTITION BY RANGE (logdate);
+</programlisting>
       </para>
-     </listitem>
 
-     <listitem>
+      <note>
+       <para>
+        To decide when to use multiple columns in the partition key for range
+        partitioning, consider whether queries accessing the partitioned
+        in question will include conditions that involve multiple columns,
+        especially the columns being considered to be the partition key.
+        If so, the optimizer can create a plan that will scan fewer partitions
+        if a query's conditions are such that there is equality constraint on
+        leading partition key columns, because they limit the number of
+        partitions of interest.  The first partition key column with
+        inequality constraint also further eliminates some partitions of
+        those chosen by equality constraints on earlier columns.
+       </para>
+      </note>
+
       <para>
-       It is currently not possible to define indexes on partitioned tables
-       that include all rows from all partitions in one global index.
-       Consequently, it is not possible to create constraints that are realized
-       using an index such as <literal>UNIQUE</>.
+       To be able to insert data into this table, one must create partitions,
+       as described below.
       </para>
      </listitem>
 
      <listitem>
       <para>
-       Since primary keys are not supported on partitioned tables,
-       foreign keys referencing partitioned tables are not supported, nor
-       are foreign key references from a partitioned table to some other table.
+       Create partitions.  Each partition's definition must specify the bounds
+       that correspond to the partitioning method and partition key of the
+       parent.  Note that specifying bounds such that the new partition's
+       values will overlap with those in one or more existing partitions will
+       cause an error.  Inserting data into into the parent table that does
+       not map to one of the existing partitions will cause an error;
+       appropriate partition must be added manually.
+      </para>
+
+      <para>
+       Partitions thus created are in every way normal <productname>PostgreSQL</>
+       tables (or, possibly, foreign tables).  It is possible, for example, to
+       specify tablespace, storage parameters for each partition separately.
+      </para>
+
+      <para>
+       It is not necessary to create table constraints describing partition
+       boundary condition for partitions.  Instead, partition constraints are
+       generated implicitly from the partition bound specification whenever
+       there is need to refer to them.  Also, since any data inserted into the
+       parent table is automatically inserted into the appropriate partition,
+       it is not necessary to create triggers for the same.
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+
+CREATE TABLE measurement_y2006m03 PARTITION OF measurement
+    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')
+
+...
+CREATE TABLE measurement_y2007m11 PARTITION OF measurement
+    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01')
+
+CREATE TABLE measurement_y2007m12 PARTITION OF measurement
+    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
+    TABLESPACE fasttablespace;
+
+CREATE TABLE measurement_y2008m01 PARTITION OF measurement
+    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
+    TABLESPACE fasttablespace
+    WITH (parallel_workers = 4);
+</programlisting>
       </para>
+
+      <note>
+       <para>
+        To implement sub-partitioning, specify the
+        <literal>PARTITION BY</literal> clause in the commands used to create
+        individual partitions, for example:
+
+<programlisting>
+CREATE TABLE measurement_y2006m02 PARTITION OF measurement
+    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
+    PARTITION BY RANGE (peaktemp);
+</programlisting>
+
+        After creating partitions of <structname>measurement_y2006m02</>,
+        any data inserted into <structname>measurement</> that is mapped to
+        <structname>measurement_y2006m02</> will be further redirected to one
+        of its partitions based on the <structfield>peaktemp</> column.
+        Partition key specified may overlap with the parent's partition key,
+        although care must be taken when specifying the bounds of
+        sub-partitions such that the accepted set of data constitutes a
+        subset of what a partition's own bounds allows; the system does not
+        try to check if that's really the case.
+       </para>
+      </note>
      </listitem>
 
      <listitem>
       <para>
-       Row triggers, if necessary, must be defined on individual partitions, not
-       the partitioned table as it is currently not supported.
+       Create an index on the key column(s),
+       as well as any other indexes you might want for every partition. 
+       Note that it is currently not supported to propagate index definition
+       from the master partitioned table to its partitions; in fact, it is
+       not possible to define indexes on partitioned tables in the first
+       place.  This might change in future releases.
+
+<programlisting>
+CREATE INDEX ON measurement_y2006m02 (logdate);
+CREATE INDEX ON measurement_y2006m03 (logdate);
+...
+CREATE INDEX ON measurement_y2007m11 (logdate);
+CREATE INDEX ON measurement_y2007m12 (logdate);
+CREATE INDEX ON measurement_y2008m01 (logdate);
+</programlisting>
       </para>
      </listitem>
-    </itemizedlist>
+
+      <listitem>
+       <para>
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
+       </para>
+      </listitem>
+    </orderedlist>
    </para>
 
    <para>
-    A detailed example that shows how to use partitioned tables is discussed in
-    the next chapter.
+    In the above example we would be creating a new partition each month, so
+    it might be wise to write a script that generates the required DDL
+    automatically.
    </para>
-   
-  </sect1>
+   </sect3>
 
-  <sect1 id="ddl-partitioning">
-   <title>Partitioning</title>
+   <sect3 id="ddl-partitioning-declarative-maintenance">
+    <title>Partition Maintenance</title>
 
-   <indexterm>
-    <primary>partitioning</primary>
-   </indexterm>
+    <para>
+      Normally the set of partitions established when initially defining the
+      the table are not intended to remain static.  It is common to want to
+      remove old partitions of data and periodically add new partitions for
+      new data. One of the most important advantages of partitioning is
+      precisely that it allows this otherwise painful task to be executed
+      nearly instantaneously by manipulating the partition structure, rather
+      than physically moving large amounts of data around.
+    </para>
 
-   <indexterm>
-    <primary>table</primary>
-    <secondary>partitioning</secondary>
-   </indexterm>
+    <para>
+     The simplest option for removing old data is simply to drop the partition
+     that is no longer necessary:
+<programlisting>
+DROP TABLE measurement_y2006m02;
+</programlisting>
+     This can very quickly delete millions of records because it doesn't have
+     to individually delete every record.  Note however that the above command
+     requires taking an <literal>ACCESS EXCLUSIVE</literal> lock on the parent
+     table.
+    </para>
 
    <para>
-    <productname>PostgreSQL</productname> supports basic table
-    partitioning. This section describes why and how to implement
-    partitioning as part of your database design.
-   </para>
+     Another option that is often preferable is to remove the partition from
+     the partitioned table but retain access to it as a table in its own
+     right:
 
-   <sect2 id="ddl-partitioning-overview">
-     <title>Overview</title>
+<programlisting>
+ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+</programlisting>
+
+     This allows further operations to be performed on the data before
+     it is dropped. For example, this is often a useful time to back up
+     the data using <command>COPY</>, <application>pg_dump</>, or
+     similar tools. It might also be a useful time to aggregate data
+     into smaller formats, perform other data manipulations, or run
+     reports.
+   </para>
 
    <para>
-    Partitioning refers to splitting what is logically one large table
-    into smaller physical pieces.
-    Partitioning can provide several benefits:
-   <itemizedlist>
-    <listitem>
-     <para>
-      Query performance can be improved dramatically in certain situations,
-      particularly when most of the heavily accessed rows of the table are in a
-      single partition or a small number of partitions.  The partitioning
-      substitutes for leading columns of indexes, reducing index size and
-      making it more likely that the heavily-used parts of the indexes
-      fit in memory.
-     </para>
-    </listitem>
+     Similarly we can add a new partition to handle new data. We can create an
+     empty partition in the partitioned table just as the original partitions
+     were created above:
 
-    <listitem>
-     <para>
-      When queries or updates access a large percentage of a single
-      partition, performance can be improved by taking advantage
-      of sequential scan of that partition instead of using an
-      index and random access reads scattered across the whole table.
-     </para>
-    </listitem>
+<programlisting>
+CREATE TABLE measurement_y2008m02 PARTITION OF measurement
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
+    TABLESPACE fasttablespace;
+</programlisting>
 
-    <listitem>
-     <para>
-      Bulk loads and deletes can be accomplished by adding or removing
-      partitions, if that requirement is planned into the partitioning design.
-      <command>ALTER TABLE NO INHERIT</> or <command>ALTER TABLE DETACH PARTITION</>
-      and <command>DROP TABLE</> are both far faster than a bulk operation.
-      These commands also entirely avoid the <command>VACUUM</command>
-      overhead caused by a bulk <command>DELETE</>.
-     </para>
-    </listitem>
+     As an alternative, it is sometimes more convenient to create the
+     new table outside the partition structure, and make it a proper
+     partition later. This allows the data to be loaded, checked, and
+     transformed prior to it appearing in the partitioned table:
 
-    <listitem>
-     <para>
-      Seldom-used data can be migrated to cheaper and slower storage media.
-     </para>
-    </listitem>
-   </itemizedlist>
+<programlisting>
+CREATE TABLE measurement_y2008m02
+  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
+  TABLESPACE fasttablespace;
 
-    The benefits will normally be worthwhile only when a table would
-    otherwise be very large. The exact point at which a table will
-    benefit from partitioning depends on the application, although a
-    rule of thumb is that the size of the table should exceed the physical
-    memory of the database server.
-   </para>
+ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
+   CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
 
-   <para>
-    Currently, <productname>PostgreSQL</productname> supports partitioning
-    using two methods:
+\copy measurement_y2008m02 from 'measurement_y2008m02'
+-- possibly some other data preparation work
 
-    <variablelist>
-     <varlistentry>
-      <term>Using Table Inheritance</term>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+</programlisting>
+    </para>
 
-      <listitem>
-       <para>
-        Each partition must be created as a child table of a single parent
-        table.  The parent table itself is normally empty; it exists just to
-        represent the entire data set.  You should be familiar with
-        inheritance (see <xref linkend="ddl-inherit">) before attempting to
-        set up partitioning with it.  This was the only method to implement
-        partitioning in older versions.
-       </para>
-      </listitem>
-     </varlistentry>
+    <tip>
+     <para>
+      Before running the <command>ATTACH PARTITION</> command, it is
+      recommended to create a <literal>CHECK</> constraint on the table to
+      be attached describing the desired partition constraint.  Using the
+      same, system is able to skip the scan to validate the implicit
+      partition constraint. Without such a constraint, the table will be
+      scanned to validate the partition constraint, while holding an
+      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
+      One may want to drop the constraint after <command>ATTACH PARTITION</>
+      is finished, because it is no longer necessary.
+     </para>
+    </tip>
+   </sect3>
 
-     <varlistentry>
-      <term>Using Partitioned Tables</term>
+   <sect3 id="ddl-partitioning-declarative-limitations">
+    <title>Limitations</title>
 
-      <listitem>
-       <para>
-        See last section for some general information:
-        <xref linkend="ddl-partitioned-tables">
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+   <para>
+    There are currently the following limitations of using partitioned tables:
+    <itemizedlist>
+     <listitem>
+      <para>
+       It is currently not possible to add same set of indexes on all partitions
+       automatically. Indexes must be added to each partition with separate
+       commands.
+      </para>
+     </listitem>
 
-   <para>
-    The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname> using either of the above mentioned
-    methods, although the latter provides dedicated syntax for each:
+     <listitem>
+      <para>
+       It is currently not possible to define indexes on partitioned tables
+       that include all rows from all partitions in one global index.
+       Consequently, it is not possible to create constraints that are realized
+       using an index such as <literal>UNIQUE</>.
+      </para>
+     </listitem>
 
-    <variablelist>
-     <varlistentry>
-      <term>Range Partitioning</term>
+     <listitem>
+      <para>
+       Since primary keys are not supported on partitioned tables,
+       foreign keys referencing partitioned tables are not supported, nor
+       are foreign key references from a partitioned table to some other table.
+      </para>
+     </listitem>
 
-      <listitem>
-       <para>
-        The table is partitioned into <quote>ranges</quote> defined
-        by a key column or set of columns, with no overlap between
-        the ranges of values assigned to different partitions.  For
-        example one might partition by date ranges, or by ranges of
-        identifiers for particular business objects.
-       </para>
-      </listitem>
-     </varlistentry>
+     <listitem>
+      <para>
+       <command>INSERT</command> statements with <literal>ON CONFLICT</>
+       clause are currently not allowed on partitioned tables.
+      </para>
+     </listitem>
 
-     <varlistentry>
-      <term>List Partitioning</term>
+     <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>
-        The table is partitioned by explicitly listing which key values
-        appear in each partition.
-       </para>
-      </listitem>
-     </varlistentry>
-    </variablelist>
-   </para>
+     <listitem>
+      <para>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </para>
+     </listitem>
+    </itemizedlist>
+    </para>
+    </sect3>
    </sect2>
 
-   <sect2 id="ddl-partitioning-implementation">
-     <title>Implementing Partitioning</title>
+   <sect2 id="ddl-partitioning-implementation-inheritance">
+    <title>Implementation Using Inheritance</title>
+    <para>
+     In some cases, one may want to add columns to partitions that are not
+     present in the parent table which is not possible to do with the above
+     method.  For such cases, partitioning can be implemented using
+     inheritance (see <xref linkend="ddl-inherit">).
+    </para>
+
+    <sect3 id="ddl-partitioning-inheritance-example">
+     <title>Example</title>
 
     <para>
-     To set up a partitioned table using inheritance, do the following:
+     We use the same <structname>measurement</structname> table we used
+     above.  To implement it as a partitioned table using inheritance, do the
+     following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
@@ -3076,6 +3399,11 @@ VALUES ('Albany', NULL, NULL, 'NY');
         be applied equally to all partitions.  There is no point
         in defining any indexes or unique constraints on it, either.
        </para>
+
+       <para>
+        In case of our example, master table is the original
+        <structname>measurement</structname> as originally defined.
+       </para>
       </listitem>
 
       <listitem>
@@ -3090,12 +3418,27 @@ VALUES ('Albany', NULL, NULL, 'NY');
         are in every way normal <productname>PostgreSQL</> tables
         (or, possibly, foreign tables).
        </para>
+
+       <para>
+        This solves one of our problems: deleting old data. Each
+        month, all we will need to do is perform a <command>DROP
+        TABLE</command> on the oldest child table and create a new
+        child table for the new month's data.
+<programlisting>
+CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
+CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
+...
+CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
+CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
+CREATE TABLE measurement_y2008m01 () INHERITS (measurement);
+</programlisting>
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        Add table constraints to the partition tables to define the
-        allowed key values in each partition.
+        Add non-overlapping table constraints to the partition tables to
+        define the allowed key values in each partition.
        </para>
 
        <para>
@@ -3117,230 +3460,53 @@ CHECK ( outletID BETWEEN 200 AND 300 )
        </para>
 
        <para>
-        Note that there is no difference in
-        syntax between range and list partitioning; those terms are
-        descriptive only.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        For each partition, create an index on the key column(s),
-        as well as any other indexes you might want.  (The key index is
-        not strictly necessary, but in most scenarios it is helpful.
-        If you intend the key values to be unique then you should
-        always create a unique or primary-key constraint for each
-        partition.)
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Optionally, define a trigger or rule to redirect data inserted into
-        the master table to the appropriate partition.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion">
-        configuration parameter is not disabled in
-        <filename>postgresql.conf</>.
-        If it is, queries will not be optimized as desired.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     To use partitioned tables, do the following:
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        Create <quote>master</quote> table as a partitioned table by
-        specifying the <literal>PARTITION BY</literal> clause, which includes
-        the partitioning method (<literal>RANGE</literal> or
-        <literal>LIST</literal>) and the list of column(s) to use as the
-        partition key.  To be able to insert data into the table, one must
-        create partitions, as described below.
-       </para>
-
-       <note>
-        <para>
-         To decide when to use multiple columns in the partition key for range
-         partitioning, consider whether queries accessing the partitioned
-         in question will include conditions that involve multiple columns,
-         especially the columns being considered to be the partition key.
-         If so, the optimizer can create a plan that will scan fewer partitions
-         if a query's conditions are such that there is equality constraint on
-         leading partition key columns, because they limit the number of
-         partitions of interest.  The first partition key column with
-         inequality constraint also further eliminates some partitions of
-         those chosen by equality constraints on earlier columns.
-        </para>
-       </note>
-      </listitem>
-
-      <listitem>
-       <para>
-        Create partitions of the master partitioned table, with the partition
-        bounds specified for each partition matching the partitioning method
-        and partition key of the master table.  Note that specifying partition
-        bounds such that the new partition's values will overlap with one or
-        more existing partitions will cause an error.  It is only after
-        creating partitions that one is able to insert data into the master
-        partitioned table, provided it maps to one of the existing partitions.
-        If a data row does not map to any of the existing partitions, it will
-        cause an error.
-       </para>
-
-       <para>
-        Partitions thus created are also in every way normal
-        <productname>PostgreSQL</> tables (or, possibly, foreign tables),
-        whereas partitioned tables differ in a number of ways.
-       </para>
-
-       <para>
-        It is not necessary to create table constraints for partitions.
-        Instead, partition constraints are generated implicitly whenever
-        there is a need to refer to them.  Also, since any data inserted into
-        the master partitioned table is automatically inserted into the
-        appropriate partition, it is not necessary to create triggers for the
-        same.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Just like with inheritance, create an index on the key column(s),
-        as well as any other indexes you might want for every partition. 
-        Note that it is currently not supported to propagate index definition
-        from the master partitioned table to its partitions; in fact, it is
-        not possible to define indexes on partitioned tables in the first
-        place.  This might change in future releases.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Currently, partitioned tables also depend on constraint exclusion
-        for query optimization, so ensure that the
-        <xref linkend="guc-constraint-exclusion"> configuration parameter is
-        not disabled in <filename>postgresql.conf</>.  This might change in
-        future releases.
-       </para>
-      </listitem>
-
-     </orderedlist>
-    </para>
-
-    <para>
-     For example, suppose we are constructing a database for a large
-     ice cream company. The company measures peak temperatures every
-     day as well as ice cream sales in each region. Conceptually,
-     we want a table like:
-
-<programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-);
-</programlisting>
-
-     We know that most queries will access just the last week's, month's or
-     quarter's data, since the main use of this table will be to prepare
-     online reports for management.
-     To reduce the amount of old data that needs to be stored, we
-     decide to only keep the most recent 3 years worth of data. At the
-     beginning of each month we will remove the oldest month's data.
-    </para>
-
-    <para>
-     In this situation we can use partitioning to help us meet all of our
-     different requirements for the measurements table. Following the
-     steps outlined above for both methods, partitioning can be set up as
-     follows:
-    </para>
-
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
-       <para>
-        The master table is the <structname>measurement</> table, declared
-        exactly as above.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        Next we create one partition for each active month:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2006m03 ( ) INHERITS (measurement);
-...
-CREATE TABLE measurement_y2007m11 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2007m12 ( ) INHERITS (measurement);
-CREATE TABLE measurement_y2008m01 ( ) INHERITS (measurement);
-</programlisting>
-
-        Each of the partitions are complete tables in their own right,
-        but they inherit their definitions from the
-        <structname>measurement</> table.
-       </para>
-
-       <para>
-        This solves one of our problems: deleting old data. Each
-        month, all we will need to do is perform a <command>DROP
-        TABLE</command> on the oldest child table and create a new
-        child table for the new month's data.
-       </para>
-      </listitem>
-
-      <listitem>
-       <para>
-        We must provide non-overlapping table constraints.  Rather than
-        just creating the partition tables as above, the table creation
-        script should really be:
+        It would be better to instead create partitions as follows:
 
 <programlisting>
 CREATE TABLE measurement_y2006m02 (
     CHECK ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2006m03 (
     CHECK ( logdate &gt;= DATE '2006-03-01' AND logdate &lt; DATE '2006-04-01' )
 ) INHERITS (measurement);
+
 ...
 CREATE TABLE measurement_y2007m11 (
     CHECK ( logdate &gt;= DATE '2007-11-01' AND logdate &lt; DATE '2007-12-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2007m12 (
     CHECK ( logdate &gt;= DATE '2007-12-01' AND logdate &lt; DATE '2008-01-01' )
 ) INHERITS (measurement);
+
 CREATE TABLE measurement_y2008m01 (
     CHECK ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
 ) INHERITS (measurement);
 </programlisting>
        </para>
+
+       <para>
+        Note that there is no difference in syntax between range and list
+        partitioning; those terms are descriptive only.
+       </para>
       </listitem>
 
       <listitem>
        <para>
-        We probably need indexes on the key columns too:
-
+        For each partition, create an index on the key column(s),
+        as well as any other indexes you might want.  (The key index is
+        not strictly necessary, but in most scenarios it is helpful.
+        If you intend the key values to be unique then you should
+        always create a unique or primary-key constraint for each
+        partition.)
 <programlisting>
 CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
 CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
-...
 CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
 CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
 CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);
 </programlisting>
-
-        We choose not to add further indexes at this time.
        </para>
       </listitem>
 
@@ -3363,7 +3529,9 @@ END;
 $$
 LANGUAGE plpgsql;
 </programlisting>
+       </para>
 
+       <para>
         After creating the function, we create a trigger which
         calls the trigger function:
 
@@ -3425,151 +3593,88 @@ LANGUAGE plpgsql;
          of this example.
         </para>
        </note>
-      </listitem>
-     </orderedlist>
-    </para>
-
-    <para>
-     Steps when using a partitioned table are as follows:
-    </para>
 
-    <para>
-     <orderedlist spacing="compact">
-      <listitem>
        <para>
-        Create the <structname>measurement</> table as a partitioned table:
+        A different approach to redirecting inserts into the appropriate
+        partition table is to set up rules, instead of a trigger, on the
+        master table.  For example:
 
 <programlisting>
-CREATE TABLE measurement (
-    city_id         int not null,
-    logdate         date not null,
-    peaktemp        int,
-    unitsales       int
-) PARTITION BY RANGE (logdate);
+CREATE RULE measurement_insert_y2006m02 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
+...
+CREATE RULE measurement_insert_y2008m01 AS
+ON INSERT TO measurement WHERE
+    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
+DO INSTEAD
+    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
 </programlisting>
+
+        A rule has significantly more overhead than a trigger, but the overhead
+        is paid once per query rather than once per row, so this method might
+        be advantageous for bulk-insert situations.  In most cases, however,
+        the trigger method will offer better performance.
        </para>
-      </listitem>
 
-      <listitem>
        <para>
-        Then create partitions as follows:
+        Be aware that <command>COPY</> ignores rules.  If you want to
+        use <command>COPY</> to insert data, you'll need to copy into the
+        correct partition table rather than into the master.  <command>COPY</>
+        does fire triggers, so you can use it normally if you use the trigger
+        approach.
+       </para>
 
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');
-CREATE TABLE measurement_y2006m03 PARTITION OF measurement
-    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');
-...
-CREATE TABLE measurement_y2007m11 PARTITION OF measurement
-    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');
-CREATE TABLE measurement_y2007m12 PARTITION OF measurement
-    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01');
-CREATE TABLE measurement_y2008m01 PARTITION OF measurement
-    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01');
-</programlisting>
+       <para>
+        Another disadvantage of the rule approach is that there is no simple
+        way to force an error if the set of rules doesn't cover the insertion
+        date; the data will silently go into the master table instead.
        </para>
       </listitem>
 
       <listitem>
        <para>
-        Create indexes on the key columns just like in case of inheritance
-        partitions.
+        Ensure that the <xref linkend="guc-constraint-exclusion">
+        configuration parameter is not disabled in
+        <filename>postgresql.conf</>.
+        If it is, queries will not be optimized as desired.
        </para>
       </listitem>
      </orderedlist>
-
-     <note>
-      <para>
-       To implement sub-partitioning, specify the
-       <literal>PARTITION BY</literal> clause in the commands used to create
-       individual partitions, for example:
-
-<programlisting>
-CREATE TABLE measurement_y2006m02 PARTITION OF measurement
-    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
-    PARTITION BY RANGE (peaktemp);
-</programlisting>
-
-       After creating partitions of <structname>measurement_y2006m02</>, any
-       data inserted into <structname>measurement</> that is mapped to
-       <structname>measurement_y2006m02</> will be further redirected to one
-       of its partitions based on the <structfield>peaktemp</> column.
-       Partition key specified may overlap with the parent's partition key,
-       although care must be taken when specifying the bounds of sub-partitions
-       such that the accepted set of data constitutes a subset of what a
-       partition's own bounds allows; the system does not try to check if
-       that's really the case.
-      </para>
-     </note>
     </para>
 
     <para>
-     As we can see, a complex partitioning scheme could require a
-     substantial amount of DDL, although significantly less when using
-     partitioned tables.  In the above example we would be creating a new
-     partition each month, so it might be wise to write a script that
-     generates the required DDL automatically.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-managing-partitions">
-   <title>Managing Partitions</title>
-
-   <para>
-     Normally the set of partitions established when initially
-     defining the table are not intended to remain static. It is
-     common to want to remove old partitions of data and periodically
-     add new partitions for new data. One of the most important
-     advantages of partitioning is precisely that it allows this
-     otherwise painful task to be executed nearly instantaneously by
-     manipulating the partition structure, rather than physically moving large
-     amounts of data around.
-   </para>
-
-   <para>
-    Both the inheritance-based and partitioned table methods allow this to
-    be done, although the latter requires taking an <literal>ACCESS EXCLUSIVE</literal>
-    lock on the master table for various commands mentioned below.
-   </para>
+     As we can see, a complex partitioning scheme could require a
+     substantial amount of DDL.  In the above example we would be creating
+     a new partition each month, so it might be wise to write a script that
+     generates the required DDL automatically.
+    </para>
+   </sect3>
 
-   <para>
-     The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary, which works using both methods of
-     partitioning:
+   <sect3 id="ddl-partitioning-inheritance-maintenance">
+    <title>Partition Maintenance</title>
+    <para>
+     To remove old data quickly, simply to drop the partition that is no
+     longer necessary:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
-     This can very quickly delete millions of records because it doesn't have
-     to individually delete every record.
-   </para>
+    </para>
 
    <para>
-     Another option that is often preferable is to remove the partition from
-     the partitioned table but retain access to it as a table in its own
-     right:
-<programlisting>
-ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
-</programlisting>
-
-     When using a partitioned table:
+    To remove the partition from the partitioned table but retain access to
+    it as a table in its own right:
 
 <programlisting>
-ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
+ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
-
-     This allows further operations to be performed on the data before
-     it is dropped. For example, this is often a useful time to back up
-     the data using <command>COPY</>, <application>pg_dump</>, or
-     similar tools. It might also be a useful time to aggregate data
-     into smaller formats, perform other data manipulations, or run
-     reports.
    </para>
 
    <para>
-     Similarly we can add a new partition to handle new data. We can create an
-     empty partition in the partitioned table just as the original partitions
-     were created above:
+     To add a new partition to handle new data, create an empty partition just
+     as the original partitions were created above:
 
 <programlisting>
 CREATE TABLE measurement_y2008m02 (
@@ -3577,52 +3682,80 @@ CREATE TABLE measurement_y2008m02 (
 ) INHERITS (measurement);
 </programlisting>
 
-    When using a partitioned table:
-
-<programlisting>
-CREATE TABLE measurement_y2008m02 PARTITION OF measurement
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01');
-</programlisting>
-
-     As an alternative, it is sometimes more convenient to create the
-     new table outside the partition structure, and make it a proper
-     partition later. This allows the data to be loaded, checked, and
-     transformed prior to it appearing in the partitioned table:
+     Alternatively, one may created the new table outside the partition
+     structure, and make it a partition after data is loaded, checked,
+     and transformed.
 
 <programlisting>
 CREATE TABLE measurement_y2008m02
   (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
+
 ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
    CHECK ( logdate &gt;= DATE '2008-02-01' AND logdate &lt; DATE '2008-03-01' );
+
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
+
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
 </programlisting>
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-inheritance-caveats">
+    <title>Caveats</title>
+
+   <para>
+    The following caveats apply to partitioned tables implemented using
+    inheritance:
+   <itemizedlist>
+    <listitem>
+     <para>
+      There is no automatic way to verify that all of the
+      <literal>CHECK</literal> constraints are mutually
+      exclusive.  It is safer to create code that generates
+      partitions and creates and/or modifies associated objects than
+      to write each by hand.
+     </para>
+    </listitem>
 
-     The last of the above commands when using a partitioned table would be:
+    <listitem>
+     <para>
+      The schemes shown here assume that the partition key column(s)
+      of a row never change, or at least do not change enough to require
+      it to move to another partition.  An <command>UPDATE</> that attempts
+      to do that will fail because of the <literal>CHECK</> constraints.
+      If you need to handle such cases, you can put suitable update triggers
+      on the partition tables, but it makes management of the structure
+      much more complicated.
+     </para>
+    </listitem>
 
+    <listitem>
+     <para>
+      If you are using manual <command>VACUUM</command> or
+      <command>ANALYZE</command> commands, don't forget that
+      you need to run them on each partition individually. A command like:
 <programlisting>
-ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
-    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
+ANALYZE measurement;
 </programlisting>
-    </para>
+      will only process the master table.
+     </para>
+    </listitem>
 
-    <tip>
+    <listitem>
      <para>
-      Before running the <command>ATTACH PARTITION</> command, it is
-      recommended to create a <literal>CHECK</> constraint on the table to
-      be attached describing the desired partition constraint.  Using the
-      same, system is able to skip the scan to validate the implicit
-      partition constraint. Without such a constraint, the table will be
-      scanned to validate the partition constraint, while holding an
-      <literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
-      One may want to drop the constraint after <command>ATTACH PARTITION</>
-      is finished, because it is no longer necessary.
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
+      action is only taken in case of unique violations on the specified
+      target relation, not its child relations.
      </para>
-    </tip>
-   </sect2>
+    </listitem>
+   </itemizedlist>
+   </para>
+   </sect3>
+  </sect2>
 
-   <sect2 id="ddl-partitioning-constraint-exclusion">
+  <sect2 id="ddl-partitioning-constraint-exclusion">
    <title>Partitioning and Constraint Exclusion</title>
 
    <indexterm>
@@ -3632,7 +3765,8 @@ ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    <para>
     <firstterm>Constraint exclusion</> is a query optimization technique
     that improves performance for partitioned tables defined in the
-    fashion described above.  As an example:
+    fashion described above (both declarative partitioned tables and those
+    implemented using inheritance).  As an example:
 
 <programlisting>
 SET constraint_exclusion = on;
@@ -3715,153 +3849,6 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
     are unlikely to benefit.
    </para>
 
-   <note>
-    <para>
-     Currently, constraint exclusion is also used for partitioned tables.
-     However, we did not create any <literal>CHECK</literal> constraints
-     for individual partitions as seen above.  In this case, the optimizer
-     uses internally generated constraint for every partition.
-    </para>
-   </note>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-alternatives">
-   <title>Alternative Partitioning Methods</title>
-
-    <para>
-     A different approach to redirecting inserts into the appropriate
-     partition table is to set up rules, instead of a trigger, on the
-     master table (unless it is a partitioned table).  For example:
-
-<programlisting>
-CREATE RULE measurement_insert_y2006m02 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2006-02-01' AND logdate &lt; DATE '2006-03-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
-...
-CREATE RULE measurement_insert_y2008m01 AS
-ON INSERT TO measurement WHERE
-    ( logdate &gt;= DATE '2008-01-01' AND logdate &lt; DATE '2008-02-01' )
-DO INSTEAD
-    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
-</programlisting>
-
-     A rule has significantly more overhead than a trigger, but the overhead
-     is paid once per query rather than once per row, so this method might be
-     advantageous for bulk-insert situations.  In most cases, however, the
-     trigger method will offer better performance.
-    </para>
-
-    <para>
-     Be aware that <command>COPY</> ignores rules.  If you want to
-     use <command>COPY</> to insert data, you'll need to copy into the correct
-     partition table rather than into the master.  <command>COPY</> does fire
-     triggers, so you can use it normally if you use the trigger approach.
-    </para>
-
-    <para>
-     Another disadvantage of the rule approach is that there is no simple
-     way to force an error if the set of rules doesn't cover the insertion
-     date; the data will silently go into the master table instead.
-    </para>
-
-    <para>
-     Partitioning can also be arranged using a <literal>UNION ALL</literal>
-     view, instead of table inheritance.  For example,
-
-<programlisting>
-CREATE VIEW measurement AS
-          SELECT * FROM measurement_y2006m02
-UNION ALL SELECT * FROM measurement_y2006m03
-...
-UNION ALL SELECT * FROM measurement_y2007m11
-UNION ALL SELECT * FROM measurement_y2007m12
-UNION ALL SELECT * FROM measurement_y2008m01;
-</programlisting>
-
-     However, the need to recreate the view adds an extra step to adding and
-     dropping individual partitions of the data set.  In practice this
-     method has little to recommend it compared to using inheritance.
-    </para>
-
-   </sect2>
-
-   <sect2 id="ddl-partitioning-caveats">
-   <title>Caveats</title>
-
-   <para>
-    The following caveats apply to using inheritance to implement partitioning:
-   <itemizedlist>
-    <listitem>
-     <para>
-      There is no automatic way to verify that all of the
-      <literal>CHECK</literal> constraints are mutually
-      exclusive.  It is safer to create code that generates
-      partitions and creates and/or modifies associated objects than
-      to write each by hand.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      The schemes shown here assume that the partition key column(s)
-      of a row never change, or at least do not change enough to require
-      it to move to another partition.  An <command>UPDATE</> that attempts
-      to do that will fail because of the <literal>CHECK</> constraints.
-      If you need to handle such cases, you can put suitable update triggers
-      on the partition tables, but it makes management of the structure
-      much more complicated.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      If you are using manual <command>VACUUM</command> or
-      <command>ANALYZE</command> commands, don't forget that
-      you need to run them on each partition individually. A command like:
-<programlisting>
-ANALYZE measurement;
-</programlisting>
-      will only process the master table.
-     </para>
-    </listitem>
-
-    <listitem>
-     <para>
-      <command>INSERT</command> statements with <literal>ON CONFLICT</>
-      clauses are unlikely to work as expected, as the <literal>ON CONFLICT</>
-      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.
-     </para>
-    </listitem>
-
-   </itemizedlist>
-   </para>
-
    <para>
     The following caveats apply to constraint exclusion, which is currently
     used by both inheritance and partitioned tables:
@@ -3901,10 +3888,78 @@ ANALYZE measurement;
       don't try to use many thousands of partitions.
      </para>
     </listitem>
-
    </itemizedlist>
    </para>
   </sect2>
+
+   <sect2 id="ddl-partitioning-alternatives">
+   <title>Alternative Partitioning Methods</title>
+
+   <sect3 id="ddl-partitioning-alternatives-union-all">
+    <title>Using UNION ALL view</title>
+    <para>
+     Partitioning can also be arranged using a <literal>UNION ALL</literal>
+     view, instead of table inheritance.  For example,
+
+<programlisting>
+CREATE VIEW measurement AS
+          SELECT * FROM measurement_y2006m02
+UNION ALL SELECT * FROM measurement_y2006m03
+...
+UNION ALL SELECT * FROM measurement_y2007m11
+UNION ALL SELECT * FROM measurement_y2007m12
+UNION ALL SELECT * FROM measurement_y2008m01;
+</programlisting>
+
+     However, the need to recreate the view adds an extra step to adding and
+     dropping individual partitions of the data set.  In practice this
+     method has little to recommend it compared to using inheritance.
+    </para>
+   </sect3>
+
+   <sect3 id="ddl-partitioning-alternatives-brin-index">
+    <title>Accessing Tables Using BRIN Index</title>
+    <para>
+     <acronym>BRIN</acronym>, which stands for Block Range Index, is
+     designed for handling very large tables in which certain columns
+     have some natural physical location within the table.  For example,
+     in the <structname>measurement</structname> table, the entries for
+     earlier times (<structfield>logdate</structfield> column) will appear
+     earlier in the table most of the time.  A table storing a ZIP code
+     column might have all codes for a city grouped together naturally.
+    </para>
+
+    <para>
+     In case of <structname>measurement</structname> table, one may consider
+     adding a minmax <acronym>BRIN</acronym> index on the
+     <structfield>logdate</structfield> column.
+
+<programlisting>
+CREATE INDEX ON measurement USING brin (logdate date_minmax_ops);
+</programlisting>
+
+     In this case, specifying <literal>date_minmax_ops</literal> is not
+     necessary; it is shown for clarity.
+    </para>
+
+    <para>
+     <acronym>BRIN</acronym> indexes leverage this locality of data and
+     store summary information for a range of consecutive pages and keep
+     it updated as the data is added or removed.  Because a
+     <acronym>BRIN</acronym> index is very small, scanning the index adds
+     adds little overhead compared to a sequential scan, but may avoid
+     scanning large parts of the table that are known not to contain
+     matching tuples.  That is often why table partitioning is used. Thus,
+     <acronym>BRIN</acronym> indexes provide a subset of benefits that
+     parttioning provides with much less upfront setup.
+    </para>
+
+    <para>
+     See <xref linkend="brin"> for more details.
+    </para>
+   </sect3>
+
+   </sect2>
  </sect1>
 
  <sect1 id="ddl-foreign-data">
-- 
2.11.0


--------------434C61868FD2E7C005F82712
Content-Type: text/x-diff;
 name="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="0002-Add-a-note-about-DROP-NOT-NULL-and-partitions.patch"



^ permalink  raw  reply  [nested|flat] 50+ messages in thread

* [PATCH v18] Avoid orphaned objects dependencies
@ 2024-03-29 15:43 Bertrand Drouvot <[email protected]>
  0 siblings, 0 replies; 50+ messages in thread

From: Bertrand Drouvot @ 2024-03-29 15:43 UTC (permalink / raw)

It's currently possible to create orphaned objects dependencies, for example:

Scenario 1:

session 1: begin; drop schema schem;
session 2: create a function in the schema schem
session 1: commit;

With the above, the function created in session 2 would be linked to a non
existing schema.

Scenario 2:

session 1: begin; create a function in the schema schem
session 2: drop schema schem;
session 1: commit;

With the above, the function created in session 1 would be linked to a non
existing schema.

To avoid those scenarios, a new lock (that conflicts with a lock taken by DROP)
has been put in place before the dependencies are being recorded. With this in
place, the drop schema in scenario 2 would be locked.

Also, after the new lock attempt, the patch checks that the object still exists:
with this in place session 2 in scenario 1 would be locked and would report an
error once session 1 committs (that would not be the case should session 1 abort
the transaction).

If the object is dropped before the new lock attempt is triggered then the patch
would also report an error (but with less details).

The patch takes into account any type of objects except the ones that are pinned
(they are not droppable because the system requires it).

A special case is done for objects that belong to the RelationRelationId class.
For those, we should be in one of the two following cases that would already
prevent the relation to be dropped:

1. The relation is already locked (could be an existing relation or a relation
that we are creating).

2. The relation is protected indirectly (i.e an index protected by a lock on
its table, a table protected by a lock on a function that depends the table...)

To avoid any risks for the RelationRelationId class case, we acquire a lock if
there is none. That may add unnecessary lock for 2. but that's worth it.

The patch adds a few tests for some dependency cases (that would currently produce
orphaned objects):

- schema and function (as the above scenarios)
- alter a dependency (function and schema)
- function and arg type
- function and return type
- function and function
- domain and domain
- table and type
- server and foreign data wrapper
---
 src/backend/catalog/aclchk.c                  |   1 +
 src/backend/catalog/dependency.c              | 212 ++++++++++++++++++
 src/backend/catalog/heap.c                    |   7 +
 src/backend/catalog/index.c                   |  26 +++
 src/backend/catalog/objectaddress.c           |  57 +++++
 src/backend/catalog/pg_aggregate.c            |   9 +
 src/backend/catalog/pg_attrdef.c              |   1 +
 src/backend/catalog/pg_cast.c                 |   5 +
 src/backend/catalog/pg_collation.c            |   1 +
 src/backend/catalog/pg_constraint.c           |  26 +++
 src/backend/catalog/pg_conversion.c           |   2 +
 src/backend/catalog/pg_depend.c               |  39 +++-
 src/backend/catalog/pg_operator.c             |  19 ++
 src/backend/catalog/pg_proc.c                 |  17 +-
 src/backend/catalog/pg_publication.c          |  11 +
 src/backend/catalog/pg_range.c                |   6 +
 src/backend/catalog/pg_type.c                 |  39 ++++
 src/backend/catalog/toasting.c                |   1 +
 src/backend/commands/alter.c                  |   4 +
 src/backend/commands/amcmds.c                 |   1 +
 src/backend/commands/cluster.c                |   7 +
 src/backend/commands/event_trigger.c          |   1 +
 src/backend/commands/extension.c              |   5 +
 src/backend/commands/foreigncmds.c            |   7 +
 src/backend/commands/functioncmds.c           |   6 +
 src/backend/commands/indexcmds.c              |   2 +
 src/backend/commands/opclasscmds.c            |  18 ++
 src/backend/commands/operatorcmds.c           |  30 +++
 src/backend/commands/policy.c                 |   2 +
 src/backend/commands/proclang.c               |   3 +
 src/backend/commands/sequence.c               |   2 +
 src/backend/commands/statscmds.c              |  10 +
 src/backend/commands/tablecmds.c              |  24 +-
 src/backend/commands/trigger.c                |  29 ++-
 src/backend/commands/tsearchcmds.c            |  73 +++++-
 src/backend/commands/typecmds.c               |  84 +++++++
 src/backend/rewrite/rewriteDefine.c           |   1 +
 src/backend/utils/errcodes.txt                |   1 +
 src/include/catalog/dependency.h              |   3 +
 src/include/catalog/objectaddress.h           |   1 +
 .../expected/test_dependencies_locks.out      | 129 +++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/test_dependencies_locks.spec        |  89 ++++++++
 .../test_oat_hooks/expected/alter_table.out   |   4 +-
 .../expected/test_oat_hooks.out               |   2 +
 src/test/regress/expected/alter_table.out     |  11 +-
 46 files changed, 1009 insertions(+), 20 deletions(-)
  40.0% src/backend/catalog/
  29.8% src/backend/commands/
  16.8% src/test/isolation/expected/
  10.5% src/test/isolation/specs/

diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index b196294fb2..126a648597 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -1341,6 +1341,7 @@ SetDefaultACL(InternalDefaultACL *iacls)
 				referenced.objectId = iacls->nspid;
 				referenced.objectSubId = 0;
 
+				LockNotPinnedObject(NamespaceRelationId, iacls->nspid);
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 			}
 		}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 096b68c7f3..5149e28aa0 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1519,6 +1519,81 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 	}
 }
 
+/*
+ * LockNotPinnedObjectById
+ *
+ * Lock the object that we are about to record a dependency on.
+ * After it's locked, verify that it hasn't been dropped while we
+ * weren't looking.  If the object has been dropped, this function
+ * does not return!
+ */
+void
+LockNotPinnedObjectById(const ObjectAddress *object)
+{
+	char	   *object_description = NULL;
+
+	if (isObjectPinned(object))
+		return;
+
+	object_description = getObjectDescription(object, true);
+
+	if (object->classId == RelationRelationId)
+	{
+		Assert(!IsSharedRelation(object->objectId));
+
+		/*
+		 * We must be in one of the two following cases that would already
+		 * prevent the relation to be dropped: 1. The relation is already
+		 * locked (could be an existing relation or a relation that we are
+		 * creating). 2. The relation is protected indirectly (i.e an index
+		 * protected by a lock on its table, a table protected by a lock on a
+		 * function that depends of the table...). To avoid any risks, acquire
+		 * a lock if there is none. That may add unnecessary lock for 2. but
+		 * that's worth it.
+		 */
+		if (!CheckRelationOidLockedByMe(object->objectId, AccessShareLock, true))
+			LockRelationOid(object->objectId, AccessShareLock);
+	}
+	else
+	{
+		/* assume we should lock the whole object not a sub-object */
+		LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock);
+	}
+
+	/* check if object still exists */
+	if (!ObjectByIdExist(object))
+	{
+		if (object_description)
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("%s does not exist", object_description)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("a dependent object does not exist")));
+	}
+
+	if (object_description)
+		pfree(object_description);
+
+	return;
+}
+
+/*
+ * LockNotPinnedObject
+ *
+ * Lock the object that we are about to record a dependency on.
+ */
+void
+LockNotPinnedObject(Oid classid, Oid objid)
+{
+	ObjectAddress object;
+
+	ObjectAddressSet(object, classid, objid);
+
+	LockNotPinnedObjectById(&object);
+}
+
 /*
  * ReleaseDeletionLock - release an object deletion lock
  *
@@ -1730,6 +1805,7 @@ find_expr_references_walker(Node *node,
 		if (rte->rtekind == RTE_RELATION)
 		{
 			/* If it's a plain relation, reference this column */
+			LockNotPinnedObject(RelationRelationId, rte->relid);
 			add_object_address(RelationRelationId, rte->relid, var->varattno,
 							   context->addrs);
 		}
@@ -1756,6 +1832,7 @@ find_expr_references_walker(Node *node,
 		Oid			objoid;
 
 		/* A constant must depend on the constant's datatype */
+		LockNotPinnedObject(TypeRelationId, con->consttype);
 		add_object_address(TypeRelationId, con->consttype, 0,
 						   context->addrs);
 
@@ -1767,8 +1844,11 @@ find_expr_references_walker(Node *node,
 		 */
 		if (OidIsValid(con->constcollid) &&
 			con->constcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, con->constcollid);
 			add_object_address(CollationRelationId, con->constcollid, 0,
 							   context->addrs);
+		}
 
 		/*
 		 * If it's a regclass or similar literal referring to an existing
@@ -1785,59 +1865,83 @@ find_expr_references_walker(Node *node,
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(PROCOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(ProcedureRelationId, objoid);
 						add_object_address(ProcedureRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGOPEROID:
 				case REGOPERATOROID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(OPEROID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(OperatorRelationId, objoid);
 						add_object_address(OperatorRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGCLASSOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(RELOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(RelationRelationId, objoid);
 						add_object_address(RelationRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGTYPEOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(TYPEOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(TypeRelationId, objoid);
 						add_object_address(TypeRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGCOLLATIONOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(COLLOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(CollationRelationId, objoid);
 						add_object_address(CollationRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGCONFIGOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(TSCONFIGOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(TSConfigRelationId, objoid);
 						add_object_address(TSConfigRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGDICTIONARYOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(TSDICTOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(TSDictionaryRelationId, objoid);
 						add_object_address(TSDictionaryRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 
 				case REGNAMESPACEOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(NAMESPACEOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(NamespaceRelationId, objoid);
 						add_object_address(NamespaceRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 
 					/*
@@ -1859,18 +1963,23 @@ find_expr_references_walker(Node *node,
 		Param	   *param = (Param *) node;
 
 		/* A parameter must depend on the parameter's datatype */
+		LockNotPinnedObject(TypeRelationId, param->paramtype);
 		add_object_address(TypeRelationId, param->paramtype, 0,
 						   context->addrs);
 		/* and its collation, just as for Consts */
 		if (OidIsValid(param->paramcollid) &&
 			param->paramcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, param->paramcollid);
 			add_object_address(CollationRelationId, param->paramcollid, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, FuncExpr))
 	{
 		FuncExpr   *funcexpr = (FuncExpr *) node;
 
+		LockNotPinnedObject(ProcedureRelationId, funcexpr->funcid);
 		add_object_address(ProcedureRelationId, funcexpr->funcid, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1879,6 +1988,7 @@ find_expr_references_walker(Node *node,
 	{
 		OpExpr	   *opexpr = (OpExpr *) node;
 
+		LockNotPinnedObject(OperatorRelationId, opexpr->opno);
 		add_object_address(OperatorRelationId, opexpr->opno, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1887,6 +1997,7 @@ find_expr_references_walker(Node *node,
 	{
 		DistinctExpr *distinctexpr = (DistinctExpr *) node;
 
+		LockNotPinnedObject(OperatorRelationId, distinctexpr->opno);
 		add_object_address(OperatorRelationId, distinctexpr->opno, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1895,6 +2006,7 @@ find_expr_references_walker(Node *node,
 	{
 		NullIfExpr *nullifexpr = (NullIfExpr *) node;
 
+		LockNotPinnedObject(OperatorRelationId, nullifexpr->opno);
 		add_object_address(OperatorRelationId, nullifexpr->opno, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1903,6 +2015,7 @@ find_expr_references_walker(Node *node,
 	{
 		ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
 
+		LockNotPinnedObject(OperatorRelationId, opexpr->opno);
 		add_object_address(OperatorRelationId, opexpr->opno, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1911,6 +2024,7 @@ find_expr_references_walker(Node *node,
 	{
 		Aggref	   *aggref = (Aggref *) node;
 
+		LockNotPinnedObject(ProcedureRelationId, aggref->aggfnoid);
 		add_object_address(ProcedureRelationId, aggref->aggfnoid, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1919,6 +2033,7 @@ find_expr_references_walker(Node *node,
 	{
 		WindowFunc *wfunc = (WindowFunc *) node;
 
+		LockNotPinnedObject(ProcedureRelationId, wfunc->winfnoid);
 		add_object_address(ProcedureRelationId, wfunc->winfnoid, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1935,8 +2050,11 @@ find_expr_references_walker(Node *node,
 		 */
 		if (sbsref->refrestype != sbsref->refcontainertype &&
 			sbsref->refrestype != sbsref->refelemtype)
+		{
+			LockNotPinnedObject(TypeRelationId, sbsref->refrestype);
 			add_object_address(TypeRelationId, sbsref->refrestype, 0,
 							   context->addrs);
+		}
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, SubPlan))
@@ -1960,16 +2078,25 @@ find_expr_references_walker(Node *node,
 		 * anywhere else in the expression.
 		 */
 		if (OidIsValid(reltype))
+		{
+			LockNotPinnedObject(RelationRelationId, reltype);
 			add_object_address(RelationRelationId, reltype, fselect->fieldnum,
 							   context->addrs);
+		}
 		else
+		{
+			LockNotPinnedObject(TypeRelationId, fselect->resulttype);
 			add_object_address(TypeRelationId, fselect->resulttype, 0,
 							   context->addrs);
+		}
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(fselect->resultcollid) &&
 			fselect->resultcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, fselect->resultcollid);
 			add_object_address(CollationRelationId, fselect->resultcollid, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, FieldStore))
 	{
@@ -1980,53 +2107,76 @@ find_expr_references_walker(Node *node,
 		if (OidIsValid(reltype))
 		{
 			ListCell   *l;
+			bool	locked = false;
 
 			foreach(l, fstore->fieldnums)
+			{
+				if (!locked)
+				{
+					LockNotPinnedObject(RelationRelationId, reltype);
+					locked = true;
+				}
 				add_object_address(RelationRelationId, reltype, lfirst_int(l),
 								   context->addrs);
+			}
 		}
 		else
+		{
+			LockNotPinnedObject(TypeRelationId, fstore->resulttype);
 			add_object_address(TypeRelationId, fstore->resulttype, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, RelabelType))
 	{
 		RelabelType *relab = (RelabelType *) node;
 
 		/* since there is no function dependency, need to depend on type */
+		LockNotPinnedObject(TypeRelationId, relab->resulttype);
 		add_object_address(TypeRelationId, relab->resulttype, 0,
 						   context->addrs);
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(relab->resultcollid) &&
 			relab->resultcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, relab->resultcollid);
 			add_object_address(CollationRelationId, relab->resultcollid, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, CoerceViaIO))
 	{
 		CoerceViaIO *iocoerce = (CoerceViaIO *) node;
 
 		/* since there is no exposed function, need to depend on type */
+		LockNotPinnedObject(TypeRelationId, iocoerce->resulttype);
 		add_object_address(TypeRelationId, iocoerce->resulttype, 0,
 						   context->addrs);
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(iocoerce->resultcollid) &&
 			iocoerce->resultcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, iocoerce->resultcollid);
 			add_object_address(CollationRelationId, iocoerce->resultcollid, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, ArrayCoerceExpr))
 	{
 		ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
 
 		/* as above, depend on type */
+		LockNotPinnedObject(TypeRelationId, acoerce->resulttype);
 		add_object_address(TypeRelationId, acoerce->resulttype, 0,
 						   context->addrs);
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(acoerce->resultcollid) &&
 			acoerce->resultcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, acoerce->resultcollid);
 			add_object_address(CollationRelationId, acoerce->resultcollid, 0,
 							   context->addrs);
+		}
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, ConvertRowtypeExpr))
@@ -2034,6 +2184,7 @@ find_expr_references_walker(Node *node,
 		ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
 
 		/* since there is no function dependency, need to depend on type */
+		LockNotPinnedObject(TypeRelationId, cvt->resulttype);
 		add_object_address(TypeRelationId, cvt->resulttype, 0,
 						   context->addrs);
 	}
@@ -2041,6 +2192,7 @@ find_expr_references_walker(Node *node,
 	{
 		CollateExpr *coll = (CollateExpr *) node;
 
+		LockNotPinnedObject(CollationRelationId, coll->collOid);
 		add_object_address(CollationRelationId, coll->collOid, 0,
 						   context->addrs);
 	}
@@ -2048,6 +2200,7 @@ find_expr_references_walker(Node *node,
 	{
 		RowExpr    *rowexpr = (RowExpr *) node;
 
+		LockNotPinnedObject(TypeRelationId, rowexpr->row_typeid);
 		add_object_address(TypeRelationId, rowexpr->row_typeid, 0,
 						   context->addrs);
 	}
@@ -2058,11 +2211,13 @@ find_expr_references_walker(Node *node,
 
 		foreach(l, rcexpr->opnos)
 		{
+			LockNotPinnedObject(OperatorRelationId, lfirst_oid(l));
 			add_object_address(OperatorRelationId, lfirst_oid(l), 0,
 							   context->addrs);
 		}
 		foreach(l, rcexpr->opfamilies)
 		{
+			LockNotPinnedObject(OperatorFamilyRelationId, lfirst_oid(l));
 			add_object_address(OperatorFamilyRelationId, lfirst_oid(l), 0,
 							   context->addrs);
 		}
@@ -2072,6 +2227,7 @@ find_expr_references_walker(Node *node,
 	{
 		CoerceToDomain *cd = (CoerceToDomain *) node;
 
+		LockNotPinnedObject(TypeRelationId, cd->resulttype);
 		add_object_address(TypeRelationId, cd->resulttype, 0,
 						   context->addrs);
 	}
@@ -2079,6 +2235,7 @@ find_expr_references_walker(Node *node,
 	{
 		NextValueExpr *nve = (NextValueExpr *) node;
 
+		LockNotPinnedObject(RelationRelationId, nve->seqid);
 		add_object_address(RelationRelationId, nve->seqid, 0,
 						   context->addrs);
 	}
@@ -2087,19 +2244,26 @@ find_expr_references_walker(Node *node,
 		OnConflictExpr *onconflict = (OnConflictExpr *) node;
 
 		if (OidIsValid(onconflict->constraint))
+		{
+			LockNotPinnedObject(ConstraintRelationId, onconflict->constraint);
 			add_object_address(ConstraintRelationId, onconflict->constraint, 0,
 							   context->addrs);
+		}
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, SortGroupClause))
 	{
 		SortGroupClause *sgc = (SortGroupClause *) node;
 
+		LockNotPinnedObject(OperatorRelationId, sgc->eqop);
 		add_object_address(OperatorRelationId, sgc->eqop, 0,
 						   context->addrs);
 		if (OidIsValid(sgc->sortop))
+		{
+			LockNotPinnedObject(OperatorRelationId, sgc->sortop);
 			add_object_address(OperatorRelationId, sgc->sortop, 0,
 							   context->addrs);
+		}
 		return false;
 	}
 	else if (IsA(node, WindowClause))
@@ -2107,15 +2271,24 @@ find_expr_references_walker(Node *node,
 		WindowClause *wc = (WindowClause *) node;
 
 		if (OidIsValid(wc->startInRangeFunc))
+		{
+			LockNotPinnedObject(ProcedureRelationId, wc->startInRangeFunc);
 			add_object_address(ProcedureRelationId, wc->startInRangeFunc, 0,
 							   context->addrs);
+		}
 		if (OidIsValid(wc->endInRangeFunc))
+		{
+			LockNotPinnedObject(ProcedureRelationId, wc->endInRangeFunc);
 			add_object_address(ProcedureRelationId, wc->endInRangeFunc, 0,
 							   context->addrs);
+		}
 		if (OidIsValid(wc->inRangeColl) &&
 			wc->inRangeColl != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, wc->inRangeColl);
 			add_object_address(CollationRelationId, wc->inRangeColl, 0,
 							   context->addrs);
+		}
 		/* fall through to examine substructure */
 	}
 	else if (IsA(node, CTECycleClause))
@@ -2123,14 +2296,23 @@ find_expr_references_walker(Node *node,
 		CTECycleClause *cc = (CTECycleClause *) node;
 
 		if (OidIsValid(cc->cycle_mark_type))
+		{
+			LockNotPinnedObject(TypeRelationId, cc->cycle_mark_type);
 			add_object_address(TypeRelationId, cc->cycle_mark_type, 0,
 							   context->addrs);
+		}
 		if (OidIsValid(cc->cycle_mark_collation))
+		{
+			LockNotPinnedObject(CollationRelationId, cc->cycle_mark_collation);
 			add_object_address(CollationRelationId, cc->cycle_mark_collation, 0,
 							   context->addrs);
+		}
 		if (OidIsValid(cc->cycle_mark_neop))
+		{
+			LockNotPinnedObject(OperatorRelationId, cc->cycle_mark_neop);
 			add_object_address(OperatorRelationId, cc->cycle_mark_neop, 0,
 							   context->addrs);
+		}
 		/* fall through to examine substructure */
 	}
 	else if (IsA(node, Query))
@@ -2163,6 +2345,7 @@ find_expr_references_walker(Node *node,
 			switch (rte->rtekind)
 			{
 				case RTE_RELATION:
+					LockNotPinnedObject(RelationRelationId, rte->relid);
 					add_object_address(RelationRelationId, rte->relid, 0,
 									   context->addrs);
 					break;
@@ -2215,12 +2398,18 @@ find_expr_references_walker(Node *node,
 			rte = rt_fetch(query->resultRelation, query->rtable);
 			if (rte->rtekind == RTE_RELATION)
 			{
+				bool	locked = false;
 				foreach(lc, query->targetList)
 				{
 					TargetEntry *tle = (TargetEntry *) lfirst(lc);
 
 					if (tle->resjunk)
 						continue;	/* ignore junk tlist items */
+					if (!locked)
+					{
+						LockNotPinnedObject(RelationRelationId, rte->relid);
+						locked = true;
+					}
 					add_object_address(RelationRelationId, rte->relid, tle->resno,
 									   context->addrs);
 				}
@@ -2232,6 +2421,7 @@ find_expr_references_walker(Node *node,
 		 */
 		foreach(lc, query->constraintDeps)
 		{
+			LockNotPinnedObject(ConstraintRelationId, lfirst_oid(lc));
 			add_object_address(ConstraintRelationId, lfirst_oid(lc), 0,
 							   context->addrs);
 		}
@@ -2266,16 +2456,25 @@ find_expr_references_walker(Node *node,
 		 */
 		foreach(ct, rtfunc->funccoltypes)
 		{
+			LockNotPinnedObject(TypeRelationId, lfirst_oid(ct));
 			add_object_address(TypeRelationId, lfirst_oid(ct), 0,
 							   context->addrs);
 		}
 		foreach(ct, rtfunc->funccolcollations)
 		{
 			Oid			collid = lfirst_oid(ct);
+			bool	locked = false;
 
 			if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+			{
+				if (!locked)
+				{
+					LockNotPinnedObject(CollationRelationId, collid);
+					locked = true;
+				}
 				add_object_address(CollationRelationId, collid, 0,
 								   context->addrs);
+			}
 		}
 	}
 	else if (IsA(node, TableFunc))
@@ -2288,22 +2487,32 @@ find_expr_references_walker(Node *node,
 		 */
 		foreach(ct, tf->coltypes)
 		{
+			LockNotPinnedObject(TypeRelationId, lfirst_oid(ct));
 			add_object_address(TypeRelationId, lfirst_oid(ct), 0,
 							   context->addrs);
 		}
 		foreach(ct, tf->colcollations)
 		{
 			Oid			collid = lfirst_oid(ct);
+			bool 	locked = false;
 
 			if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+			{
+				if (!locked)
+				{
+					LockNotPinnedObject(CollationRelationId, collid);
+					locked = true;
+				}
 				add_object_address(CollationRelationId, collid, 0,
 								   context->addrs);
+			}
 		}
 	}
 	else if (IsA(node, TableSampleClause))
 	{
 		TableSampleClause *tsc = (TableSampleClause *) node;
 
+		LockNotPinnedObject(ProcedureRelationId, tsc->tsmhandler);
 		add_object_address(ProcedureRelationId, tsc->tsmhandler, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -2354,9 +2563,12 @@ process_function_rte_ref(RangeTblEntry *rte, AttrNumber attnum,
 
 				Assert(attnum - atts_done <= tupdesc->natts);
 				if (OidIsValid(reltype))	/* can this fail? */
+				{
+					LockNotPinnedObject(RelationRelationId, reltype);
 					add_object_address(RelationRelationId, reltype,
 									   attnum - atts_done,
 									   context->addrs);
+				}
 				return;
 			}
 			/* Nothing to do; function's result type is handled elsewhere */
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 4d760c98d1..f333a91cfe 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -836,6 +836,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		/* Add dependency info */
 		ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1);
 		ObjectAddressSet(referenced, TypeRelationId, attr->atttypid);
+		LockNotPinnedObject(TypeRelationId, attr->atttypid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 		/* The default collation is pinned, so don't bother recording it */
@@ -844,6 +845,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		{
 			ObjectAddressSet(referenced, CollationRelationId,
 							 attr->attcollation);
+			LockNotPinnedObject(CollationRelationId, attr->attcollation);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -1451,11 +1453,13 @@ heap_create_with_catalog(const char *relname,
 
 		ObjectAddressSet(referenced, NamespaceRelationId, relnamespace);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(NamespaceRelationId, relnamespace);
 
 		if (reloftypeid)
 		{
 			ObjectAddressSet(referenced, TypeRelationId, reloftypeid);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(TypeRelationId, reloftypeid);
 		}
 
 		/*
@@ -1471,6 +1475,7 @@ heap_create_with_catalog(const char *relname,
 		{
 			ObjectAddressSet(referenced, AccessMethodRelationId, accessmtd);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(AccessMethodRelationId, accessmtd);
 		}
 
 		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -3771,6 +3776,7 @@ StorePartitionKey(Relation rel,
 	{
 		ObjectAddressSet(referenced, OperatorClassRelationId, partopclass[i]);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(OperatorClassRelationId, partopclass[i]);
 
 		/* The default collation is pinned, so don't bother recording it */
 		if (OidIsValid(partcollation[i]) &&
@@ -3778,6 +3784,7 @@ StorePartitionKey(Relation rel,
 		{
 			ObjectAddressSet(referenced, CollationRelationId, partcollation[i]);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(CollationRelationId, partcollation[i]);
 		}
 	}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 221fbb4e28..1e5859ad4b 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1117,6 +1117,7 @@ index_create(Relation heapRelation,
 		else
 		{
 			bool		have_simple_col = false;
+			bool		locked_object = false;
 
 			addrs = new_object_addresses();
 
@@ -1129,6 +1130,12 @@ index_create(Relation heapRelation,
 										heapRelationId,
 										indexInfo->ii_IndexAttrNumbers[i]);
 					add_exact_object_address(&referenced, addrs);
+
+					if (!locked_object)
+					{
+						LockNotPinnedObject(RelationRelationId, heapRelationId);
+						locked_object = true;
+					}
 					have_simple_col = true;
 				}
 			}
@@ -1144,6 +1151,8 @@ index_create(Relation heapRelation,
 				ObjectAddressSet(referenced, RelationRelationId,
 								 heapRelationId);
 				add_exact_object_address(&referenced, addrs);
+
+				LockNotPinnedObject(RelationRelationId, heapRelationId);
 			}
 
 			record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO);
@@ -1159,9 +1168,13 @@ index_create(Relation heapRelation,
 		if (OidIsValid(parentIndexRelid))
 		{
 			ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid);
+
+			LockNotPinnedObject(RelationRelationId, parentIndexRelid);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 
 			ObjectAddressSet(referenced, RelationRelationId, heapRelationId);
+
+			LockNotPinnedObject(RelationRelationId, heapRelationId);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 
@@ -1177,6 +1190,7 @@ index_create(Relation heapRelation,
 			{
 				ObjectAddressSet(referenced, CollationRelationId, collationIds[i]);
 				add_exact_object_address(&referenced, addrs);
+				LockNotPinnedObject(CollationRelationId, collationIds[i]);
 			}
 		}
 
@@ -1185,6 +1199,7 @@ index_create(Relation heapRelation,
 		{
 			ObjectAddressSet(referenced, OperatorClassRelationId, opclassIds[i]);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(OperatorClassRelationId, opclassIds[i]);
 		}
 
 		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -1994,6 +2009,14 @@ index_constraint_create(Relation heapRelation,
 	 */
 	ObjectAddressSet(myself, ConstraintRelationId, conOid);
 	ObjectAddressSet(idxaddr, RelationRelationId, indexRelationId);
+
+	/*
+	 * CommandCounterIncrement() here to ensure the new constraint entry is
+	 * visible when LockNotPinnedObject() will check its existence before
+	 * recording the dependencies.
+	 */
+	CommandCounterIncrement();
+	LockNotPinnedObject(ConstraintRelationId, conOid);
 	recordDependencyOn(&idxaddr, &myself, DEPENDENCY_INTERNAL);
 
 	/*
@@ -2005,9 +2028,12 @@ index_constraint_create(Relation heapRelation,
 		ObjectAddress referenced;
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId);
+		LockNotPinnedObject(ConstraintRelationId, parentConstraintId);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 		ObjectAddressSet(referenced, RelationRelationId,
 						 RelationGetRelid(heapRelation));
+
+		LockNotPinnedObject(RelationRelationId, RelationGetRelid(heapRelation));
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index d8eb8d3dea..f3e82671df 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2595,6 +2595,63 @@ get_object_namespace(const ObjectAddress *address)
 	return oid;
 }
 
+/*
+ * ObjectByIdExist
+ *
+ * Return whether the given object exists.
+ *
+ * Works for most catalogs, if no special processing is needed.
+ */
+bool
+ObjectByIdExist(const ObjectAddress *address)
+{
+	HeapTuple	tuple;
+	int			cache;
+	const ObjectPropertyType *property;
+
+	property = get_object_property_data(address->classId);
+
+	cache = property->oid_catcache_id;
+
+	if (cache >= 0)
+	{
+		/* Fetch tuple from syscache. */
+		tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId));
+
+		if (!HeapTupleIsValid(tuple))
+		{
+			return false;
+		}
+
+		ReleaseSysCache(tuple);
+
+		return true;
+	}
+	else
+	{
+		Relation	rel;
+		ScanKeyData skey[1];
+		SysScanDesc scan;
+
+		rel = table_open(address->classId, AccessShareLock);
+
+		ScanKeyInit(&skey[0],
+					get_object_attnum_oid(address->classId),
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(address->objectId));
+
+		scan = systable_beginscan(rel, get_object_oid_index(address->classId), true,
+								  NULL, 1, skey);
+
+		/* we expect exactly one match */
+		tuple = systable_getnext(scan);
+		systable_endscan(scan);
+		table_close(rel, AccessShareLock);
+
+		return (HeapTupleIsValid(tuple));
+	}
+}
+
 /*
  * Return ObjectType for the given object type as given by
  * getObjectTypeDescription; if no valid ObjectType code exists, but it's a
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index bcf4050f5b..ce5865fac6 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -748,12 +748,14 @@ AggregateCreate(const char *aggName,
 	/* Depends on transition function */
 	ObjectAddressSet(referenced, ProcedureRelationId, transfn);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(ProcedureRelationId, transfn);
 
 	/* Depends on final function, if any */
 	if (OidIsValid(finalfn))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, finalfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, finalfn);
 	}
 
 	/* Depends on combine function, if any */
@@ -761,6 +763,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, combinefn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, combinefn);
 	}
 
 	/* Depends on serialization function, if any */
@@ -768,6 +771,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, serialfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, serialfn);
 	}
 
 	/* Depends on deserialization function, if any */
@@ -775,6 +779,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, deserialfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, deserialfn);
 	}
 
 	/* Depends on forward transition function, if any */
@@ -782,6 +787,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, mtransfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, mtransfn);
 	}
 
 	/* Depends on inverse transition function, if any */
@@ -789,6 +795,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, minvtransfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, minvtransfn);
 	}
 
 	/* Depends on final function, if any */
@@ -796,6 +803,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, mfinalfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, mfinalfn);
 	}
 
 	/* Depends on sort operator, if any */
@@ -803,6 +811,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, OperatorRelationId, sortop);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(OperatorRelationId, sortop);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c
index 91dafc8102..91da30c504 100644
--- a/src/backend/catalog/pg_attrdef.c
+++ b/src/backend/catalog/pg_attrdef.c
@@ -178,6 +178,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	colobject.objectId = RelationGetRelid(rel);
 	colobject.objectSubId = attnum;
 
+	LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
 	recordDependencyOn(&defobject, &colobject,
 					   attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 1773c9c549..90513a39ac 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -97,16 +97,19 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
 	/* dependency on source type */
 	ObjectAddressSet(referenced, TypeRelationId, sourcetypeid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, sourcetypeid);
 
 	/* dependency on target type */
 	ObjectAddressSet(referenced, TypeRelationId, targettypeid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, targettypeid);
 
 	/* dependency on function */
 	if (OidIsValid(funcid))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, funcid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, funcid);
 	}
 
 	/* dependencies on casts required for function */
@@ -114,11 +117,13 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
 	{
 		ObjectAddressSet(referenced, CastRelationId, incastid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(CastRelationId, incastid);
 	}
 	if (OidIsValid(outcastid))
 	{
 		ObjectAddressSet(referenced, CastRelationId, outcastid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(CastRelationId, outcastid);
 	}
 
 	record_object_address_dependencies(&myself, addrs, behavior);
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index 469635b358..fa6fd7bf79 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -218,6 +218,7 @@ CollationCreate(const char *collname, Oid collnamespace,
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = collnamespace;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(NamespaceRelationId, collnamespace);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* create dependency on owner */
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 0c6ac0be41..cee0bdc280 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -257,17 +257,26 @@ CreateConstraintEntry(const char *constraintName,
 
 		if (constraintNTotalKeys > 0)
 		{
+			bool		locked_object = false;
+
 			for (i = 0; i < constraintNTotalKeys; i++)
 			{
 				ObjectAddressSubSet(relobject, RelationRelationId, relId,
 									constraintKey[i]);
 				add_exact_object_address(&relobject, addrs_auto);
+
+				if (!locked_object)
+				{
+					LockNotPinnedObject(RelationRelationId, relId);
+					locked_object = true;
+				}
 			}
 		}
 		else
 		{
 			ObjectAddressSet(relobject, RelationRelationId, relId);
 			add_exact_object_address(&relobject, addrs_auto);
+			LockNotPinnedObject(RelationRelationId, relId);
 		}
 	}
 
@@ -280,6 +289,7 @@ CreateConstraintEntry(const char *constraintName,
 
 		ObjectAddressSet(domobject, TypeRelationId, domainId);
 		add_exact_object_address(&domobject, addrs_auto);
+		LockNotPinnedObject(TypeRelationId, domainId);
 	}
 
 	record_object_address_dependencies(&conobject, addrs_auto,
@@ -299,17 +309,26 @@ CreateConstraintEntry(const char *constraintName,
 
 		if (foreignNKeys > 0)
 		{
+			bool		locked_object = false;
+
 			for (i = 0; i < foreignNKeys; i++)
 			{
 				ObjectAddressSubSet(relobject, RelationRelationId,
 									foreignRelId, foreignKey[i]);
 				add_exact_object_address(&relobject, addrs_normal);
+
+				if (!locked_object)
+				{
+					LockNotPinnedObject(RelationRelationId, foreignRelId);
+					locked_object = true;
+				}
 			}
 		}
 		else
 		{
 			ObjectAddressSet(relobject, RelationRelationId, foreignRelId);
 			add_exact_object_address(&relobject, addrs_normal);
+			LockNotPinnedObject(RelationRelationId, foreignRelId);
 		}
 	}
 
@@ -325,6 +344,7 @@ CreateConstraintEntry(const char *constraintName,
 
 		ObjectAddressSet(relobject, RelationRelationId, indexRelId);
 		add_exact_object_address(&relobject, addrs_normal);
+		LockNotPinnedObject(RelationRelationId, indexRelId);
 	}
 
 	if (foreignNKeys > 0)
@@ -344,15 +364,18 @@ CreateConstraintEntry(const char *constraintName,
 		{
 			oprobject.objectId = pfEqOp[i];
 			add_exact_object_address(&oprobject, addrs_normal);
+			LockNotPinnedObject(OperatorRelationId, pfEqOp[i]);
 			if (ppEqOp[i] != pfEqOp[i])
 			{
 				oprobject.objectId = ppEqOp[i];
 				add_exact_object_address(&oprobject, addrs_normal);
+				LockNotPinnedObject(OperatorRelationId, ppEqOp[i]);
 			}
 			if (ffEqOp[i] != pfEqOp[i])
 			{
 				oprobject.objectId = ffEqOp[i];
 				add_exact_object_address(&oprobject, addrs_normal);
+				LockNotPinnedObject(OperatorRelationId, ffEqOp[i]);
 			}
 		}
 	}
@@ -1110,9 +1133,12 @@ ConstraintSetParentConstraint(Oid childConstrId,
 		ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
+		LockNotPinnedObject(ConstraintRelationId, parentConstrId);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
 
 		ObjectAddressSet(referenced, RelationRelationId, childTableId);
+
+		LockNotPinnedObject(RelationRelationId, childTableId);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 	else
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 04cc375cae..5ac365ebd0 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -116,12 +116,14 @@ ConversionCreate(const char *conname, Oid connamespace,
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = conproc;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ProcedureRelationId, conproc);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* create dependency on namespace */
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = connamespace;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(NamespaceRelationId, connamespace);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* create dependency on owner */
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index c8b11f887e..fdafffb893 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -20,19 +20,20 @@
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/pg_auth_members.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_extension.h"
 #include "catalog/partition.h"
 #include "commands/extension.h"
 #include "miscadmin.h"
+#include "storage/lmgr.h"
+#include "storage/lock.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 
 
-static bool isObjectPinned(const ObjectAddress *object);
-
 
 /*
  * Record a dependency between 2 objects via their respective ObjectAddress.
@@ -99,6 +100,37 @@ recordMultipleDependencies(const ObjectAddress *depender,
 	slot_init_count = 0;
 	for (i = 0; i < nreferenced; i++, referenced++)
 	{
+#ifdef USE_ASSERT_CHECKING
+		if (!isObjectPinned(referenced))
+		{
+			if (referenced->classId != RelationRelationId)
+			{
+				LOCKTAG		tag;
+
+				SET_LOCKTAG_OBJECT(tag,
+								   MyDatabaseId,
+								   referenced->classId,
+								   referenced->objectId,
+								   0);
+				/* assert the referenced object is locked */
+				Assert(LockHeldByMe(&tag, AccessShareLock, false));
+			}
+			else
+			{
+				Assert(!IsSharedRelation(referenced->objectId));
+
+				/*
+				 * Assert the referenced object is locked if it should be
+				 * visible (see the comment related to LockNotPinnedObject()
+				 * in TypeCreate()).
+				 */
+				Assert(!ObjectByIdExist(referenced) ||
+					   CheckRelationOidLockedByMe(referenced->objectId,
+												  AccessShareLock, true));
+			}
+		}
+#endif
+
 		/*
 		 * If the referenced object is pinned by the system, there's no real
 		 * need to record dependencies on it.  This saves lots of space in
@@ -238,6 +270,7 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
 		extension.objectId = CurrentExtensionObject;
 		extension.objectSubId = 0;
 
+		LockNotPinnedObject(ExtensionRelationId, CurrentExtensionObject);
 		recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
 	}
 }
@@ -705,7 +738,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
  * The passed subId, if any, is ignored; we assume that only whole objects
  * are pinned (and that this implies pinning their components).
  */
-static bool
+bool
 isObjectPinned(const ObjectAddress *object)
 {
 	return IsPinnedObject(object->classId, object->objectId);
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index bfcfa64346..ba6aa33218 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -251,6 +251,16 @@ OperatorShellMake(const char *operatorName,
 	values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
 	values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
 
+	/* Lock dependent objects */
+	if (OidIsValid(operatorNamespace))
+		LockNotPinnedObject(NamespaceRelationId, operatorNamespace);
+
+	if (OidIsValid(leftTypeId))
+		LockNotPinnedObject(TypeRelationId, leftTypeId);
+
+	if (OidIsValid(rightTypeId))
+		LockNotPinnedObject(TypeRelationId, rightTypeId);
+
 	/*
 	 * create a new operator tuple
 	 */
@@ -513,6 +523,15 @@ OperatorCreate(const char *operatorName,
 		CatalogTupleInsert(pg_operator_desc, tup);
 	}
 
+	/* Lock dependent objects */
+	LockNotPinnedObject(NamespaceRelationId, operatorNamespace);
+	LockNotPinnedObject(TypeRelationId, leftTypeId);
+	LockNotPinnedObject(TypeRelationId, rightTypeId);
+	LockNotPinnedObject(TypeRelationId, operResultType);
+	LockNotPinnedObject(ProcedureRelationId, procedureId);
+	LockNotPinnedObject(ProcedureRelationId, restrictionId);
+	LockNotPinnedObject(ProcedureRelationId, joinId);
+
 	/* Add dependencies for the entry */
 	address = makeOperatorDependencies(tup, true, isUpdate);
 
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index fe0490259e..c3999bdce8 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -593,6 +593,13 @@ ProcedureCreate(const char *procedureName,
 	if (is_update)
 		deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
 
+	/*
+	 * CommandCounterIncrement() here to ensure the new function entry is
+	 * visible when LockNotPinnedObject() will check its existence before
+	 * recording the dependencies.
+	 */
+	CommandCounterIncrement();
+
 	addrs = new_object_addresses();
 
 	ObjectAddressSet(myself, ProcedureRelationId, retval);
@@ -600,20 +607,24 @@ ProcedureCreate(const char *procedureName,
 	/* dependency on namespace */
 	ObjectAddressSet(referenced, NamespaceRelationId, procNamespace);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(NamespaceRelationId, procNamespace);
 
 	/* dependency on implementation language */
 	ObjectAddressSet(referenced, LanguageRelationId, languageObjectId);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(LanguageRelationId, languageObjectId);
 
 	/* dependency on return type */
 	ObjectAddressSet(referenced, TypeRelationId, returnType);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, returnType);
 
 	/* dependency on transform used by return type, if any */
 	if ((trfid = get_transform_oid(returnType, languageObjectId, true)))
 	{
 		ObjectAddressSet(referenced, TransformRelationId, trfid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(TransformRelationId, trfid);
 	}
 
 	/* dependency on parameter types */
@@ -621,12 +632,14 @@ ProcedureCreate(const char *procedureName,
 	{
 		ObjectAddressSet(referenced, TypeRelationId, allParams[i]);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(TypeRelationId, allParams[i]);
 
 		/* dependency on transform used by parameter type, if any */
 		if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
 		{
 			ObjectAddressSet(referenced, TransformRelationId, trfid);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(TransformRelationId, trfid);
 		}
 	}
 
@@ -635,6 +648,7 @@ ProcedureCreate(const char *procedureName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, prosupport);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, prosupport);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -674,9 +688,6 @@ ProcedureCreate(const char *procedureName,
 		ArrayType  *set_items = NULL;
 		int			save_nestlevel = 0;
 
-		/* Advance command counter so new tuple can be seen by validator */
-		CommandCounterIncrement();
-
 		/*
 		 * Set per-function configuration parameters so that the validation is
 		 * done with the environment the function expects.  However, if
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index b89098f5e9..2d44b8b0e5 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -441,6 +441,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 				referenced;
 	List	   *relids = NIL;
 	int			i;
+	bool		locked = false;
 
 	rel = table_open(PublicationRelRelationId, RowExclusiveLock);
 
@@ -503,10 +504,13 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 
 	/* Add dependency on the publication */
 	ObjectAddressSet(referenced, PublicationRelationId, pubid);
+	LockNotPinnedObject(PublicationRelationId, pubid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the relation */
 	ObjectAddressSet(referenced, RelationRelationId, relid);
+
+	LockNotPinnedObject(RelationRelationId, relid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the objects mentioned in the qualifications */
@@ -520,6 +524,11 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 	while ((i = bms_next_member(attnums, i)) >= 0)
 	{
 		ObjectAddressSubSet(referenced, RelationRelationId, relid, i);
+		if (!locked)
+		{
+			LockNotPinnedObject(RelationRelationId, relid);
+			locked = true;
+		}
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -705,10 +714,12 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
 
 	/* Add dependency on the publication */
 	ObjectAddressSet(referenced, PublicationRelationId, pubid);
+	LockNotPinnedObject(PublicationRelationId, pubid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the schema */
 	ObjectAddressSet(referenced, NamespaceRelationId, schemaid);
+	LockNotPinnedObject(NamespaceRelationId, schemaid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Close the table */
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index 8df73e7ab7..e1538c2146 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -70,26 +70,31 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
 
 	ObjectAddressSet(referenced, TypeRelationId, rangeSubType);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, rangeSubType);
 
 	ObjectAddressSet(referenced, OperatorClassRelationId, rangeSubOpclass);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(OperatorClassRelationId, rangeSubOpclass);
 
 	if (OidIsValid(rangeCollation))
 	{
 		ObjectAddressSet(referenced, CollationRelationId, rangeCollation);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(CollationRelationId, rangeCollation);
 	}
 
 	if (OidIsValid(rangeCanonical))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, rangeCanonical);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, rangeCanonical);
 	}
 
 	if (OidIsValid(rangeSubDiff))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, rangeSubDiff);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, rangeSubDiff);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -99,6 +104,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
 	referencing.classId = TypeRelationId;
 	referencing.objectId = multirangeTypeOid;
 	referencing.objectSubId = 0;
+	LockNotPinnedObject(TypeRelationId, rangeTypeOid);
 	recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
 
 	table_close(pg_range, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index b36f81afb9..77ff5f0603 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -157,6 +157,12 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 	 * Create dependencies.  We can/must skip this in bootstrap mode.
 	 */
 	if (!IsBootstrapProcessingMode())
+	{
+		/* Lock dependent objects */
+		LockNotPinnedObject(NamespaceRelationId, typeNamespace);
+		LockNotPinnedObject(ProcedureRelationId, F_SHELL_IN);
+		LockNotPinnedObject(ProcedureRelationId, F_SHELL_OUT);
+
 		GenerateTypeDependencies(tup,
 								 pg_type_desc,
 								 NULL,
@@ -166,6 +172,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 								 false,
 								 true,	/* make extension dependency */
 								 false);
+	}
 
 	/* Post creation hook for new shell type */
 	InvokeObjectPostCreateHook(TypeRelationId, typoid, 0);
@@ -494,6 +501,37 @@ TypeCreate(Oid newTypeOid,
 	 * Create dependencies.  We can/must skip this in bootstrap mode.
 	 */
 	if (!IsBootstrapProcessingMode())
+	{
+		/*
+		 * CommandCounterIncrement() here to ensure the new type entry is
+		 * visible when LockNotPinnedObject() will check its existence before
+		 * recording the dependencies.
+		 */
+		CommandCounterIncrement();
+
+		/* Lock dependent objects */
+		LockNotPinnedObject(NamespaceRelationId, typeNamespace);
+		LockNotPinnedObject(ProcedureRelationId, inputProcedure);
+		LockNotPinnedObject(ProcedureRelationId, outputProcedure);
+		LockNotPinnedObject(ProcedureRelationId, receiveProcedure);
+		LockNotPinnedObject(ProcedureRelationId, sendProcedure);
+		LockNotPinnedObject(ProcedureRelationId, typmodinProcedure);
+		LockNotPinnedObject(ProcedureRelationId, typmodoutProcedure);
+		LockNotPinnedObject(ProcedureRelationId, analyzeProcedure);
+		LockNotPinnedObject(ProcedureRelationId, subscriptProcedure);
+		LockNotPinnedObject(TypeRelationId, baseType);
+		LockNotPinnedObject(CollationRelationId, typeCollation);
+		LockNotPinnedObject(TypeRelationId, elementType);
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on relationOid as relationOid is set to an InvalidOid or to a new
+		 * Oid not added to pg_class yet (In heap_create_with_catalog(),
+		 * AddNewRelationType() is called before AddNewRelationTuple()).
+		 */
+		if (relationKind == RELKIND_COMPOSITE_TYPE)
+			LockNotPinnedObject(TypeRelationId, typeObjectId);
+
 		GenerateTypeDependencies(tup,
 								 pg_type_desc,
 								 (defaultTypeBin ?
@@ -505,6 +543,7 @@ TypeCreate(Oid newTypeOid,
 								 isDependentType,
 								 true,	/* make extension dependency */
 								 rebuildDeps);
+	}
 
 	/* Post creation hook for new type */
 	InvokeObjectPostCreateHook(TypeRelationId, typeObjectId, 0);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 874a8fc89a..b8fb22af6a 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -383,6 +383,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 		toastobject.objectId = toast_relid;
 		toastobject.objectSubId = 0;
 
+		LockNotPinnedObject(RelationRelationId, relOid);
 		recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
 	}
 
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 78c1d4e1b8..e068b127c2 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -499,7 +499,10 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
 		currexts = getAutoExtensionsOfObject(address.classId,
 											 address.objectId);
 		if (!list_member_oid(currexts, refAddr.objectId))
+		{
+			LockNotPinnedObject(refAddr.classId, refAddr.objectId);
 			recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
+		}
 	}
 
 	return address;
@@ -803,6 +806,7 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
 	pfree(replaces);
 
 	/* update dependency to point to the new schema */
+	LockNotPinnedObject(NamespaceRelationId, nspOid);
 	if (changeDependencyFor(classId, objid,
 							NamespaceRelationId, oldNspOid, nspOid) != 1)
 		elog(ERROR, "could not change schema dependency for object %u",
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 58ed9d216c..eb9b86d563 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -104,6 +104,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	referenced.objectId = amhandler;
 	referenced.objectSubId = 0;
 
+	LockNotPinnedObject(ProcedureRelationId, amhandler);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	recordDependencyOnCurrentExtension(&myself, false);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index effd395086..60b18f9f7c 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1271,6 +1271,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 	 */
 	if (relam1 != relam2)
 	{
+		LockNotPinnedObject(AccessMethodRelationId, relam2);
 		if (changeDependencyFor(RelationRelationId,
 								r1,
 								AccessMethodRelationId,
@@ -1279,6 +1280,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 			elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
 				 get_namespace_name(get_rel_namespace(r1)),
 				 get_rel_name(r1));
+
+		LockNotPinnedObject(AccessMethodRelationId, relam1);
 		if (changeDependencyFor(RelationRelationId,
 								r2,
 								AccessMethodRelationId,
@@ -1380,6 +1383,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 			{
 				baseobject.objectId = r1;
 				toastobject.objectId = relform1->reltoastrelid;
+
+				LockNotPinnedObject(RelationRelationId, r1);
 				recordDependencyOn(&toastobject, &baseobject,
 								   DEPENDENCY_INTERNAL);
 			}
@@ -1388,6 +1393,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 			{
 				baseobject.objectId = r2;
 				toastobject.objectId = relform2->reltoastrelid;
+
+				LockNotPinnedObject(RelationRelationId, r2);
 				recordDependencyOn(&toastobject, &baseobject,
 								   DEPENDENCY_INTERNAL);
 			}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index edc2c988e2..14dab63ef0 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -327,6 +327,7 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = funcoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ProcedureRelationId, funcoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* Depend on extension, if any. */
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index ba540e3de5..01813ac71d 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -2035,6 +2035,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 
 	ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
 	add_exact_object_address(&nsp, refobjs);
+	LockNotPinnedObject(NamespaceRelationId, schemaOid);
 
 	foreach(lc, requiredExtensions)
 	{
@@ -2043,6 +2044,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 
 		ObjectAddressSet(otherext, ExtensionRelationId, reqext);
 		add_exact_object_address(&otherext, refobjs);
+		LockNotPinnedObject(ExtensionRelationId, reqext);
 	}
 
 	/* Record all of them (this includes duplicate elimination) */
@@ -3079,6 +3081,7 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
 	table_close(extRel, RowExclusiveLock);
 
 	/* update dependency to point to the new schema */
+	LockNotPinnedObject(NamespaceRelationId, nspOid);
 	if (changeDependencyFor(ExtensionRelationId, extensionOid,
 							NamespaceRelationId, oldNspOid, nspOid) != 1)
 		elog(ERROR, "could not change schema dependency for extension %s",
@@ -3369,6 +3372,7 @@ ApplyExtensionUpdates(Oid extensionOid,
 			otherext.objectId = reqext;
 			otherext.objectSubId = 0;
 
+			LockNotPinnedObject(ExtensionRelationId, reqext);
 			recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
 		}
 
@@ -3525,6 +3529,7 @@ ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
 		/*
 		 * OK, add the dependency.
 		 */
+		LockNotPinnedObject(extension.classId, extension.objectId);
 		recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
 
 		/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index c14e038d54..d6cced22e7 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -642,6 +642,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
 		referenced.classId = ProcedureRelationId;
 		referenced.objectId = fdwhandler;
 		referenced.objectSubId = 0;
+		LockNotPinnedObject(ProcedureRelationId, fdwhandler);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -650,6 +651,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
 		referenced.classId = ProcedureRelationId;
 		referenced.objectId = fdwvalidator;
 		referenced.objectSubId = 0;
+		LockNotPinnedObject(ProcedureRelationId, fdwvalidator);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -811,6 +813,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 			referenced.classId = ProcedureRelationId;
 			referenced.objectId = fdwhandler;
 			referenced.objectSubId = 0;
+			LockNotPinnedObject(ProcedureRelationId, fdwhandler);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 
@@ -819,6 +822,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 			referenced.classId = ProcedureRelationId;
 			referenced.objectId = fdwvalidator;
 			referenced.objectSubId = 0;
+			LockNotPinnedObject(ProcedureRelationId, fdwvalidator);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -951,6 +955,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 	referenced.classId = ForeignDataWrapperRelationId;
 	referenced.objectId = fdw->fdwid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ForeignDataWrapperRelationId, fdw->fdwid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
@@ -1195,6 +1200,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 	referenced.classId = ForeignServerRelationId;
 	referenced.objectId = srv->serverid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ForeignServerRelationId, srv->serverid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	if (OidIsValid(useId))
@@ -1472,6 +1478,7 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 	referenced.classId = ForeignServerRelationId;
 	referenced.objectId = server->serverid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ForeignServerRelationId, server->serverid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	table_close(ftrel, RowExclusiveLock);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index b9fd7683ab..e215551147 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1461,6 +1461,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 		/* Add or replace dependency on support function */
 		if (OidIsValid(procForm->prosupport))
 		{
+			LockNotPinnedObject(ProcedureRelationId, newsupport);
 			if (changeDependencyFor(ProcedureRelationId, funcOid,
 									ProcedureRelationId, procForm->prosupport,
 									newsupport) != 1)
@@ -1474,6 +1475,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 			referenced.classId = ProcedureRelationId;
 			referenced.objectId = newsupport;
 			referenced.objectSubId = 0;
+			LockNotPinnedObject(ProcedureRelationId, newsupport);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL);
 		}
 
@@ -1990,21 +1992,25 @@ CreateTransform(CreateTransformStmt *stmt)
 	/* dependency on language */
 	ObjectAddressSet(referenced, LanguageRelationId, langid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(LanguageRelationId, langid);
 
 	/* dependency on type */
 	ObjectAddressSet(referenced, TypeRelationId, typeid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, typeid);
 
 	/* dependencies on functions */
 	if (OidIsValid(fromsqlfuncid))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, fromsqlfuncid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, fromsqlfuncid);
 	}
 	if (OidIsValid(tosqlfuncid))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, tosqlfuncid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, tosqlfuncid);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d6e23caef1..ef828df294 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4524,8 +4524,10 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
 			ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
 			ObjectAddressSet(partitionTbl, RelationRelationId,
 							 partitionIdx->rd_index->indrelid);
+			LockNotPinnedObject(RelationRelationId, parentOid);
 			recordDependencyOn(&partIdx, &parentIdx,
 							   DEPENDENCY_PARTITION_PRI);
+			LockNotPinnedObject(RelationRelationId, partitionIdx->rd_index->indrelid);
 			recordDependencyOn(&partIdx, &partitionTbl,
 							   DEPENDENCY_PARTITION_SEC);
 		}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 2c325badf9..72b347e980 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -299,12 +299,14 @@ CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname,
 	referenced.classId = AccessMethodRelationId;
 	referenced.objectId = amoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(AccessMethodRelationId, amoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* dependency on namespace */
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = namespaceoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* dependency on owner */
@@ -726,18 +728,21 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = namespaceoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* dependency on opfamily */
 	referenced.classId = OperatorFamilyRelationId;
 	referenced.objectId = opfamilyoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(OperatorFamilyRelationId, opfamilyoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* dependency on indexed datatype */
 	referenced.classId = TypeRelationId;
 	referenced.objectId = typeoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(TypeRelationId, typeoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* dependency on storage datatype */
@@ -746,6 +751,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
 		referenced.classId = TypeRelationId;
 		referenced.objectId = storageoid;
 		referenced.objectSubId = 0;
+		LockNotPinnedObject(TypeRelationId, storageoid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -1487,6 +1493,13 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 
 		heap_freetuple(tup);
 
+		/*
+		 * CommandCounterIncrement() here to ensure the new operator entry is
+		 * visible when LockNotPinnedObject() will check its existence before
+		 * recording the dependencies.
+		 */
+		CommandCounterIncrement();
+
 		/* Make its dependencies */
 		myself.classId = AccessMethodOperatorRelationId;
 		myself.objectId = entryoid;
@@ -1497,6 +1510,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectSubId = 0;
 
 		/* see comments in amapi.h about dependency strength */
+		LockNotPinnedObject(OperatorRelationId, op->object);
 		recordDependencyOn(&myself, &referenced,
 						   op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
 
@@ -1505,6 +1519,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectId = op->refobjid;
 		referenced.objectSubId = 0;
 
+		LockNotPinnedObject(referenced.classId, op->refobjid);
 		recordDependencyOn(&myself, &referenced,
 						   op->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
@@ -1538,6 +1553,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 			referenced.objectId = op->sortfamily;
 			referenced.objectSubId = 0;
 
+			LockNotPinnedObject(OperatorFamilyRelationId, op->sortfamily);
 			recordDependencyOn(&myself, &referenced,
 							   op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
 		}
@@ -1621,6 +1637,7 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectSubId = 0;
 
 		/* see comments in amapi.h about dependency strength */
+		LockNotPinnedObject(ProcedureRelationId, proc->object);
 		recordDependencyOn(&myself, &referenced,
 						   proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
 
@@ -1629,6 +1646,7 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectId = proc->refobjid;
 		referenced.objectSubId = 0;
 
+		LockNotPinnedObject(referenced.classId, proc->refobjid);
 		recordDependencyOn(&myself, &referenced,
 						   proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 673648f1fc..59a4715fff 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -33,6 +33,7 @@
 
 #include "access/htup_details.h"
 #include "access/table.h"
+#include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_namespace.h"
@@ -656,11 +657,15 @@ AlterOperator(AlterOperatorStmt *stmt)
 	{
 		replaces[Anum_pg_operator_oprrest - 1] = true;
 		values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
+		if (OidIsValid(restrictionOid))
+			LockNotPinnedObject(ProcedureRelationId, restrictionOid);
 	}
 	if (updateJoin)
 	{
 		replaces[Anum_pg_operator_oprjoin - 1] = true;
 		values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
+		if (OidIsValid(joinOid))
+			LockNotPinnedObject(ProcedureRelationId, joinOid);
 	}
 	if (OidIsValid(commutatorOid))
 	{
@@ -688,6 +693,31 @@ AlterOperator(AlterOperatorStmt *stmt)
 
 	CatalogTupleUpdate(catalog, &tup->t_self, tup);
 
+
+	/* Lock dependent objects */
+	oprForm = (Form_pg_operator) GETSTRUCT(tup);
+
+	if (OidIsValid(oprForm->oprnamespace))
+		LockNotPinnedObject(NamespaceRelationId, oprForm->oprnamespace);
+
+	if (OidIsValid(oprForm->oprleft))
+		LockNotPinnedObject(TypeRelationId, oprForm->oprleft);
+
+	if (OidIsValid(oprForm->oprright))
+		LockNotPinnedObject(TypeRelationId, oprForm->oprright);
+
+	if (OidIsValid(oprForm->oprresult))
+		LockNotPinnedObject(TypeRelationId, oprForm->oprresult);
+
+	if (OidIsValid(oprForm->oprcode))
+		LockNotPinnedObject(ProcedureRelationId, oprForm->oprcode);
+
+	if (OidIsValid(oprForm->oprrest))
+		LockNotPinnedObject(ProcedureRelationId, oprForm->oprrest);
+
+	if (OidIsValid(oprForm->oprjoin))
+		LockNotPinnedObject(ProcedureRelationId, oprForm->oprjoin);
+
 	address = makeOperatorDependencies(tup, false, true);
 
 	if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 83056960fe..cf5e194792 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -722,6 +722,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
 	myself.objectId = policy_id;
 	myself.objectSubId = 0;
 
+	LockNotPinnedObject(RelationRelationId, table_id);
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
 
 	recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
@@ -1053,6 +1054,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 	myself.objectId = policy_id;
 	myself.objectSubId = 0;
 
+	LockNotPinnedObject(RelationRelationId, table_id);
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
 
 	recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 5036ac0363..f4a7e46b71 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -190,12 +190,14 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 	/* dependency on the PL handler function */
 	ObjectAddressSet(referenced, ProcedureRelationId, handlerOid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(ProcedureRelationId, handlerOid);
 
 	/* dependency on the inline handler function, if any */
 	if (OidIsValid(inlineOid))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, inlineOid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, inlineOid);
 	}
 
 	/* dependency on the validator function, if any */
@@ -203,6 +205,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, valOid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, valOid);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index b13ee2b745..31dba914b8 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1691,6 +1691,8 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
 		depobject.classId = RelationRelationId;
 		depobject.objectId = RelationGetRelid(seqrel);
 		depobject.objectSubId = 0;
+
+		LockNotPinnedObject(RelationRelationId, RelationGetRelid(tablerel));
 		recordDependencyOn(&depobject, &refobject, deptype);
 	}
 
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index a817821bf6..5b7950d582 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -88,6 +88,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	bool		build_mcv;
 	bool		build_expressions;
 	bool		requested_type = false;
+	bool		locked_object = false;
 	int			i;
 	ListCell   *cell;
 	ListCell   *cell2;
@@ -536,6 +537,12 @@ CreateStatistics(CreateStatsStmt *stmt)
 	for (i = 0; i < nattnums; i++)
 	{
 		ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
+
+		if (!locked_object)
+		{
+			LockNotPinnedObject(RelationRelationId, relid);
+			locked_object = true;
+		}
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -553,6 +560,8 @@ CreateStatistics(CreateStatsStmt *stmt)
 	if (!nattnums)
 	{
 		ObjectAddressSet(parentobject, RelationRelationId, relid);
+
+		LockNotPinnedObject(RelationRelationId, relid);
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -573,6 +582,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	 * than the underlying table(s).
 	 */
 	ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
+	LockNotPinnedObject(NamespaceRelationId, namespaceId);
 	recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL);
 
 	recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 33ea619224..4797ac5463 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3499,6 +3499,7 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid,
 	childobject.objectId = relationId;
 	childobject.objectSubId = 0;
 
+	LockNotPinnedObject(RelationRelationId, parentOid);
 	recordDependencyOn(&childobject, &parentobject,
 					   child_dependency_type(child_is_partition));
 
@@ -7401,7 +7402,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/*
 	 * Add needed dependency entries for the new column.
 	 */
+	LockNotPinnedObject(TypeRelationId, attribute->atttypid);
 	add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
+	LockNotPinnedObject(CollationRelationId, attribute->attcollation);
 	add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
 
 	/*
@@ -10481,11 +10484,16 @@ addFkConstraint(addFkConstraintSides fkside,
 
 		Assert(fkside != addFkBothSides);
 		if (fkside == addFkReferencedSide)
+		{
+			LockNotPinnedObject(ConstraintRelationId, parentConstr);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
+		}
 		else
 		{
+			LockNotPinnedObject(ConstraintRelationId, parentConstr);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
+			LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 	}
@@ -13791,7 +13799,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	table_close(attrelation, RowExclusiveLock);
 
 	/* Install dependencies on new datatype and collation */
+	LockNotPinnedObject(TypeRelationId, targettype);
 	add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
+	LockNotPinnedObject(CollationRelationId, targetcollid);
 	add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
 
 	/*
@@ -15369,6 +15379,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -15388,6 +15399,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 			   OidIsValid(rd_rel->relam));
 
 		/* Both are valid, so update the dependency */
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		changeDependencyFor(RelationRelationId, reloid,
 							AccessMethodRelationId,
 							oldAccessMethodId, rd_rel->relam);
@@ -17095,6 +17107,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	typeobj.classId = TypeRelationId;
 	typeobj.objectId = typeid;
 	typeobj.objectSubId = 0;
+	LockNotPinnedObject(TypeRelationId, typeid);
 	recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
 
 	/* Update pg_class.reloftype */
@@ -17867,14 +17880,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 
 
 		/* Update dependency on schema if caller said so */
-		if (hasDependEntry &&
-			changeDependencyFor(RelationRelationId,
+		if (hasDependEntry)
+		{
+			LockNotPinnedObject(NamespaceRelationId, newNspOid);
+			if (changeDependencyFor(RelationRelationId,
 								relOid,
 								NamespaceRelationId,
 								oldNspOid,
 								newNspOid) != 1)
-			elog(ERROR, "could not change schema dependency for relation \"%s\"",
-				 NameStr(classForm->relname));
+				elog(ERROR, "could not change schema dependency for relation \"%s\"",
+					 NameStr(classForm->relname));
+		}
 	}
 	else
 		UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 32f25f4d91..a4db8a8f67 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1019,8 +1019,6 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
 
 		CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
-
-		CommandCounterIncrement();
 	}
 	else
 		CacheInvalidateRelcacheByTuple(tuple);
@@ -1028,6 +1026,13 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	heap_freetuple(tuple);
 	table_close(pgrel, RowExclusiveLock);
 
+	/*
+	 * CommandCounterIncrement() here to ensure the new trigger entry is
+	 * visible when LockNotPinnedObject() will check its existence before
+	 * recording the dependencies.
+	 */
+	CommandCounterIncrement();
+
 	/*
 	 * If we're replacing a trigger, flush all the old dependencies before
 	 * recording new ones.
@@ -1046,6 +1051,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = funcoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ProcedureRelationId, funcoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	if (isInternal && OidIsValid(constraintOid))
@@ -1059,6 +1065,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		referenced.classId = ConstraintRelationId;
 		referenced.objectId = constraintOid;
 		referenced.objectSubId = 0;
+		LockNotPinnedObject(ConstraintRelationId, constraintOid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	}
 	else
@@ -1071,6 +1078,8 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		referenced.classId = RelationRelationId;
 		referenced.objectId = RelationGetRelid(rel);
 		referenced.objectSubId = 0;
+
+		LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 		if (OidIsValid(constrrelid))
@@ -1078,6 +1087,8 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			referenced.classId = RelationRelationId;
 			referenced.objectId = constrrelid;
 			referenced.objectSubId = 0;
+
+			LockNotPinnedObject(RelationRelationId, constrrelid);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 		}
 		/* Not possible to have an index dependency in this case */
@@ -1092,6 +1103,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			referenced.classId = ConstraintRelationId;
 			referenced.objectId = constraintOid;
 			referenced.objectSubId = 0;
+			LockNotPinnedObject(TriggerRelationId, trigoid);
 			recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
 		}
 
@@ -1101,8 +1113,11 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		if (OidIsValid(parentTriggerOid))
 		{
 			ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
+			LockNotPinnedObject(TriggerRelationId, parentTriggerOid);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
+
+			LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 	}
@@ -1111,12 +1126,19 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	if (columns != NULL)
 	{
 		int			i;
+		bool		locked_object = false;
 
 		referenced.classId = RelationRelationId;
 		referenced.objectId = RelationGetRelid(rel);
 		for (i = 0; i < ncolumns; i++)
 		{
 			referenced.objectSubId = columns[i];
+
+			if (!locked_object)
+			{
+				LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
+				locked_object = true;
+			}
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -1256,9 +1278,12 @@ TriggerSetParentTrigger(Relation trigRel,
 		ObjectAddressSet(depender, TriggerRelationId, childTrigId);
 
 		ObjectAddressSet(referenced, TriggerRelationId, parentTrigId);
+		LockNotPinnedObject(TriggerRelationId, parentTrigId);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
 
 		ObjectAddressSet(referenced, RelationRelationId, childTableId);
+
+		LockNotPinnedObject(RelationRelationId, childTableId);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 	else
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index ab16d42ad5..e36c3da102 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -214,6 +214,7 @@ DefineTSParser(List *names, List *parameters)
 	namestrcpy(&pname, prsname);
 	values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname);
 	values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
 
 	/*
 	 * loop over the definition list and extract the information we need.
@@ -224,28 +225,48 @@ DefineTSParser(List *names, List *parameters)
 
 		if (strcmp(defel->defname, "start") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prsstart - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "gettoken") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prstoken - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "end") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prsend - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "headline") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prsheadline - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "lextypes") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prslextype - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else
 			ereport(ERROR,
@@ -474,6 +495,10 @@ DefineTSDictionary(List *names, List *parameters)
 
 	CatalogTupleInsert(dictRel, tup);
 
+	/* Lock dependent objects */
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
+	LockNotPinnedObject(TSTemplateRelationId, templId);
+
 	address = makeDictionaryDependencies(tup);
 
 	/* Post creation hook for new text search dictionary */
@@ -723,6 +748,7 @@ DefineTSTemplate(List *names, List *parameters)
 	namestrcpy(&dname, tmplname);
 	values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname);
 	values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
 
 	/*
 	 * loop over the definition list and extract the information we need.
@@ -733,15 +759,23 @@ DefineTSTemplate(List *names, List *parameters)
 
 		if (strcmp(defel->defname, "init") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_template_tmplinit - 1] =
 				get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
 			nulls[Anum_pg_ts_template_tmplinit - 1] = false;
+			procoid = DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "lexize") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_template_tmpllexize - 1] =
 				get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
 			nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
+			procoid = DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else
 			ereport(ERROR,
@@ -998,6 +1032,10 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 	values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
 	values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
 
+	/* Lock dependent objects */
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
+	LockNotPinnedObject(TSParserRelationId, prsOid);
+
 	tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
 
 	CatalogTupleInsert(cfgRel, tup);
@@ -1063,6 +1101,7 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 			slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
 			slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
 
+			LockNotPinnedObject(TSDictionaryRelationId, cfgmap->mapdict);
 			ExecStoreVirtualTuple(slot[slot_stored_count]);
 			slot_stored_count++;
 
@@ -1156,9 +1195,13 @@ ObjectAddress
 AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 {
 	HeapTuple	tup;
+	Form_pg_ts_config cfg;
 	Oid			cfgId;
 	Relation	relMap;
 	ObjectAddress address;
+	ScanKeyData skey;
+	SysScanDesc scan;
+	HeapTuple	maptup;
 
 	/* Find the configuration */
 	tup = GetTSConfigTuple(stmt->cfgname);
@@ -1168,7 +1211,8 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 				 errmsg("text search configuration \"%s\" does not exist",
 						NameListToString(stmt->cfgname))));
 
-	cfgId = ((Form_pg_ts_config) GETSTRUCT(tup))->oid;
+	cfg = (Form_pg_ts_config) GETSTRUCT(tup);
+	cfgId = cfg->oid;
 
 	/* must be owner */
 	if (!object_ownercheck(TSConfigRelationId, cfgId, GetUserId()))
@@ -1183,6 +1227,28 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 	else if (stmt->tokentype)
 		DropConfigurationMapping(stmt, tup, relMap);
 
+	/* Lock dependent objects */
+
+	ScanKeyInit(&skey,
+				Anum_pg_ts_config_map_mapcfg,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(cfgId));
+
+	scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
+							  NULL, 1, &skey);
+
+	while (HeapTupleIsValid((maptup = systable_getnext(scan))))
+	{
+		Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
+
+		LockNotPinnedObject(TSDictionaryRelationId, cfgmap->mapdict);
+	}
+
+	systable_endscan(scan);
+
+	LockNotPinnedObject(NamespaceRelationId, cfg->cfgnamespace);
+	LockNotPinnedObject(TSParserRelationId, cfg->cfgparser);
+
 	/* Update dependencies */
 	makeConfigurationDependencies(tup, true, relMap);
 
@@ -1414,6 +1480,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 				repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
 				repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
 
+				LockNotPinnedObject(TSDictionaryRelationId, dictNew);
+
 				newtup = heap_modify_tuple(maptup,
 										   RelationGetDescr(relMap),
 										   repl_val, repl_null, repl_repl);
@@ -1456,6 +1524,9 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 				slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
 				slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
 
+				/* Lock dependent objects */
+				LockNotPinnedObject(TSDictionaryRelationId, dictIds[j]);
+
 				ExecStoreVirtualTuple(slot[slotCount]);
 				slotCount++;
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 6b1d238351..2059df09fd 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1812,6 +1812,7 @@ makeRangeConstructors(const char *name, Oid namespace,
 		 * that they go away silently when the type is dropped.  Note that
 		 * pg_dump depends on this choice to avoid dumping the constructors.
 		 */
+		LockNotPinnedObject(TypeRelationId, rangeOid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	}
 }
@@ -1877,6 +1878,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
 	 * that they go away silently when the type is dropped.  Note that pg_dump
 	 * depends on this choice to avoid dumping the constructors.
 	 */
+	LockNotPinnedObject(TypeRelationId, multirangeOid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	pfree(argtypes);
 
@@ -2690,6 +2692,45 @@ AlterDomainDefault(List *names, Node *defaultRaw)
 
 	CatalogTupleUpdate(rel, &tup->t_self, newtuple);
 
+	/* Lock dependent objects */
+	typTup = (Form_pg_type) GETSTRUCT(newtuple);
+
+	if (OidIsValid(typTup->typnamespace))
+		LockNotPinnedObject(NamespaceRelationId, typTup->typnamespace);
+
+	if (OidIsValid(typTup->typinput))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typinput);
+
+	if (OidIsValid(typTup->typoutput))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typoutput);
+
+	if (OidIsValid(typTup->typreceive))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typreceive);
+
+	if (OidIsValid(typTup->typsend))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typsend);
+
+	if (OidIsValid(typTup->typmodin))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typmodin);
+
+	if (OidIsValid(typTup->typmodout))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typmodout);
+
+	if (OidIsValid(typTup->typanalyze))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typanalyze);
+
+	if (OidIsValid(typTup->typsubscript))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typsubscript);
+
+	if (OidIsValid(typTup->typbasetype))
+		LockNotPinnedObject(TypeRelationId, typTup->typbasetype);
+
+	if (OidIsValid(typTup->typcollation))
+		LockNotPinnedObject(CollationRelationId, typTup->typcollation);
+
+	if (OidIsValid(typTup->typelem))
+		LockNotPinnedObject(TypeRelationId, typTup->typelem);
+
 	/* Rebuild dependencies */
 	GenerateTypeDependencies(newtuple,
 							 rel,
@@ -4296,10 +4337,13 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 	if (oldNspOid != nspOid &&
 		(isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
 		!isImplicitArray)
+	{
+		LockNotPinnedObject(NamespaceRelationId, nspOid);
 		if (changeDependencyFor(TypeRelationId, typeOid,
 								NamespaceRelationId, oldNspOid, nspOid) != 1)
 			elog(ERROR, "could not change schema dependency for type \"%s\"",
 				 format_type_be(typeOid));
+	}
 
 	InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
 
@@ -4591,6 +4635,7 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
 	SysScanDesc scan;
 	ScanKeyData key[1];
 	HeapTuple	domainTup;
+	Form_pg_type typeForm;
 
 	/* Since this function recurses, it could be driven to stack overflow */
 	check_stack_depth();
@@ -4639,6 +4684,45 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
 	newtup = heap_modify_tuple(tup, RelationGetDescr(catalog),
 							   values, nulls, replaces);
 
+	/* Lock dependent objects */
+	typeForm = (Form_pg_type) GETSTRUCT(newtup);
+
+	if (OidIsValid(typeForm->typnamespace))
+		LockNotPinnedObject(NamespaceRelationId, typeForm->typnamespace);
+
+	if (OidIsValid(typeForm->typinput))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typinput);
+
+	if (OidIsValid(typeForm->typoutput))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typoutput);
+
+	if (OidIsValid(typeForm->typreceive))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typreceive);
+
+	if (OidIsValid(typeForm->typsend))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typsend);
+
+	if (OidIsValid(typeForm->typmodin))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typmodin);
+
+	if (OidIsValid(typeForm->typmodout))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typmodout);
+
+	if (OidIsValid(typeForm->typanalyze))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typanalyze);
+
+	if (OidIsValid(typeForm->typsubscript))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typsubscript);
+
+	if (OidIsValid(typeForm->typbasetype))
+		LockNotPinnedObject(TypeRelationId, typeForm->typbasetype);
+
+	if (OidIsValid(typeForm->typcollation))
+		LockNotPinnedObject(CollationRelationId, typeForm->typcollation);
+
+	if (OidIsValid(typeForm->typelem))
+		LockNotPinnedObject(TypeRelationId, typeForm->typelem);
+
 	CatalogTupleUpdate(catalog, &newtup->t_self, newtup);
 
 	/* Rebuild dependencies for this type */
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 8aa90b0d6f..14a6468efc 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -155,6 +155,7 @@ InsertRule(const char *rulname,
 	referenced.objectId = eventrel_oid;
 	referenced.objectSubId = 0;
 
+	LockNotPinnedObject(RelationRelationId, eventrel_oid);
 	recordDependencyOn(&myself, &referenced,
 					   (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index c96aa7c49e..c664ae4977 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -277,6 +277,7 @@ Section: Class 28 - Invalid Authorization Specification
 Section: Class 2B - Dependent Privilege Descriptors Still Exist
 
 2B000    E    ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST            dependent_privilege_descriptors_still_exist
+2BP02    E    ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST                       dependent_objects_does_not_exist
 2BP01    E    ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST                          dependent_objects_still_exist
 
 Section: Class 2D - Invalid Transaction Termination
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 0ea7ccf524..73ee2c6a78 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -101,6 +101,8 @@ typedef struct ObjectAddresses ObjectAddresses;
 /* in dependency.c */
 
 extern void AcquireDeletionLock(const ObjectAddress *object, int flags);
+extern void LockNotPinnedObjectById(const ObjectAddress *object);
+extern void LockNotPinnedObject(Oid classid, Oid objid);
 
 extern void ReleaseDeletionLock(const ObjectAddress *object);
 
@@ -172,6 +174,7 @@ extern long changeDependenciesOf(Oid classId, Oid oldObjectId,
 extern long changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
 								 Oid newRefObjectId);
 
+extern bool isObjectPinned(const ObjectAddress *object);
 extern Oid	getExtensionOfObject(Oid classId, Oid objectId);
 extern List *getAutoExtensionsOfObject(Oid classId, Oid objectId);
 
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
index 630434b73c..34d5428433 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -53,6 +53,7 @@ extern void check_object_ownership(Oid roleid,
 								   Node *object, Relation relation);
 
 extern Oid	get_object_namespace(const ObjectAddress *address);
+extern bool ObjectByIdExist(const ObjectAddress *address);
 
 extern bool is_objectclass_supported(Oid class_id);
 extern const char *get_object_class_descr(Oid class_id);
diff --git a/src/test/isolation/expected/test_dependencies_locks.out b/src/test/isolation/expected/test_dependencies_locks.out
new file mode 100644
index 0000000000..9b645d7aa5
--- /dev/null
+++ b/src/test/isolation/expected/test_dependencies_locks.out
@@ -0,0 +1,129 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_create_function_in_schema s2_drop_schema s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+step s2_drop_schema: DROP SCHEMA testschema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_schema: <... completed>
+ERROR:  cannot drop schema testschema because other objects depend on it
+
+starting permutation: s2_begin s2_drop_schema s1_create_function_in_schema s2_commit
+step s2_begin: BEGIN;
+step s2_drop_schema: DROP SCHEMA testschema;
+step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_in_schema: <... completed>
+ERROR:  schema testschema does not exist
+
+starting permutation: s1_begin s1_alter_function_schema s2_drop_alterschema s1_commit
+step s1_begin: BEGIN;
+step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema;
+step s2_drop_alterschema: DROP SCHEMA alterschema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_alterschema: <... completed>
+ERROR:  cannot drop schema alterschema because other objects depend on it
+
+starting permutation: s2_begin s2_drop_alterschema s1_alter_function_schema s2_commit
+step s2_begin: BEGIN;
+step s2_drop_alterschema: DROP SCHEMA alterschema;
+step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema; <waiting ...>
+step s2_commit: COMMIT;
+step s1_alter_function_schema: <... completed>
+ERROR:  schema alterschema does not exist
+
+starting permutation: s1_begin s1_create_function_with_argtype s2_drop_foo_type s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql;
+step s2_drop_foo_type: DROP TYPE public.foo; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_foo_type: <... completed>
+ERROR:  cannot drop type foo because other objects depend on it
+
+starting permutation: s2_begin s2_drop_foo_type s1_create_function_with_argtype s2_commit
+step s2_begin: BEGIN;
+step s2_drop_foo_type: DROP TYPE public.foo;
+step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_argtype: <... completed>
+ERROR:  type foo does not exist
+
+starting permutation: s1_begin s1_create_function_with_rettype s2_drop_foo_rettype s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1;
+step s2_drop_foo_rettype: DROP DOMAIN id; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_foo_rettype: <... completed>
+ERROR:  cannot drop type id because other objects depend on it
+
+starting permutation: s2_begin s2_drop_foo_rettype s1_create_function_with_rettype s2_commit
+step s2_begin: BEGIN;
+step s2_drop_foo_rettype: DROP DOMAIN id;
+step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_rettype: <... completed>
+ERROR:  type id does not exist
+
+starting permutation: s1_begin s1_create_function_with_function s2_drop_function_f s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1;
+step s2_drop_function_f: DROP FUNCTION f(); <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_function_f: <... completed>
+ERROR:  cannot drop function f() because other objects depend on it
+
+starting permutation: s2_begin s2_drop_function_f s1_create_function_with_function s2_commit
+step s2_begin: BEGIN;
+step s2_drop_function_f: DROP FUNCTION f();
+step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_function: <... completed>
+ERROR:  function f() does not exist
+
+starting permutation: s1_begin s1_create_domain_with_domain s2_drop_domain_id s1_commit
+step s1_begin: BEGIN;
+step s1_create_domain_with_domain: CREATE DOMAIN idid as id;
+step s2_drop_domain_id: DROP DOMAIN id; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_domain_id: <... completed>
+ERROR:  cannot drop type id because other objects depend on it
+
+starting permutation: s2_begin s2_drop_domain_id s1_create_domain_with_domain s2_commit
+step s2_begin: BEGIN;
+step s2_drop_domain_id: DROP DOMAIN id;
+step s1_create_domain_with_domain: CREATE DOMAIN idid as id; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_domain_with_domain: <... completed>
+ERROR:  type id does not exist
+
+starting permutation: s1_begin s1_create_table_with_type s2_drop_footab_type s1_commit
+step s1_begin: BEGIN;
+step s1_create_table_with_type: CREATE TABLE tabtype(a footab);
+step s2_drop_footab_type: DROP TYPE public.footab; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_footab_type: <... completed>
+ERROR:  cannot drop type footab because other objects depend on it
+
+starting permutation: s2_begin s2_drop_footab_type s1_create_table_with_type s2_commit
+step s2_begin: BEGIN;
+step s2_drop_footab_type: DROP TYPE public.footab;
+step s1_create_table_with_type: CREATE TABLE tabtype(a footab); <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_table_with_type: <... completed>
+ERROR:  type footab does not exist
+
+starting permutation: s1_begin s1_create_server_with_fdw_wrapper s2_drop_fdw_wrapper s1_commit
+step s1_begin: BEGIN;
+step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper;
+step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_fdw_wrapper: <... completed>
+ERROR:  cannot drop foreign-data wrapper fdw_wrapper because other objects depend on it
+
+starting permutation: s2_begin s2_drop_fdw_wrapper s1_create_server_with_fdw_wrapper s2_commit
+step s2_begin: BEGIN;
+step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT;
+step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_server_with_fdw_wrapper: <... completed>
+ERROR:  foreign-data wrapper fdw_wrapper does not exist
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 143109aa4d..0e80dfecfb 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -115,3 +115,4 @@ test: serializable-parallel-2
 test: serializable-parallel-3
 test: matview-write-skew
 test: lock-nowait
+test: test_dependencies_locks
diff --git a/src/test/isolation/specs/test_dependencies_locks.spec b/src/test/isolation/specs/test_dependencies_locks.spec
new file mode 100644
index 0000000000..5d04dfe9dc
--- /dev/null
+++ b/src/test/isolation/specs/test_dependencies_locks.spec
@@ -0,0 +1,89 @@
+setup
+{
+  CREATE SCHEMA testschema;
+  CREATE SCHEMA alterschema;
+  CREATE TYPE public.foo as enum ('one', 'two');
+  CREATE TYPE public.footab as enum ('three', 'four');
+  CREATE DOMAIN id AS int;
+  CREATE FUNCTION f() RETURNS int LANGUAGE SQL RETURN 1;
+  CREATE FUNCTION public.falter() RETURNS int LANGUAGE SQL RETURN 1;
+  CREATE FOREIGN DATA WRAPPER fdw_wrapper;
+}
+
+teardown
+{
+  DROP FUNCTION IF EXISTS testschema.foo();
+  DROP FUNCTION IF EXISTS fooargtype(num foo);
+  DROP FUNCTION IF EXISTS footrettype();
+  DROP FUNCTION IF EXISTS foofunc();
+  DROP FUNCTION IF EXISTS public.falter();
+  DROP FUNCTION IF EXISTS alterschema.falter();
+  DROP DOMAIN IF EXISTS idid;
+  DROP SERVER IF EXISTS srv_fdw_wrapper;
+  DROP TABLE IF EXISTS tabtype;
+  DROP SCHEMA IF EXISTS testschema;
+  DROP SCHEMA IF EXISTS alterschema;
+  DROP TYPE IF EXISTS public.foo;
+  DROP TYPE IF EXISTS public.footab;
+  DROP DOMAIN IF EXISTS id;
+  DROP FUNCTION IF EXISTS f();
+  DROP FOREIGN DATA WRAPPER IF EXISTS fdw_wrapper;
+}
+
+session "s1"
+
+step "s1_begin" { BEGIN; }
+step "s1_create_function_in_schema" { CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; }
+step "s1_create_function_with_argtype" { CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; }
+step "s1_create_function_with_rettype" { CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; }
+step "s1_create_function_with_function" { CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; }
+step "s1_alter_function_schema" { ALTER FUNCTION public.falter() SET SCHEMA alterschema; }
+step "s1_create_domain_with_domain" { CREATE DOMAIN idid as id; }
+step "s1_create_table_with_type" { CREATE TABLE tabtype(a footab); }
+step "s1_create_server_with_fdw_wrapper" { CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+
+step "s2_begin" { BEGIN; }
+step "s2_drop_schema" { DROP SCHEMA testschema; }
+step "s2_drop_alterschema" { DROP SCHEMA alterschema; }
+step "s2_drop_foo_type" { DROP TYPE public.foo; }
+step "s2_drop_foo_rettype" { DROP DOMAIN id; }
+step "s2_drop_footab_type" { DROP TYPE public.footab; }
+step "s2_drop_function_f" { DROP FUNCTION f(); }
+step "s2_drop_domain_id" { DROP DOMAIN id; }
+step "s2_drop_fdw_wrapper" { DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; }
+step "s2_commit" { COMMIT; }
+
+# function - schema
+permutation "s1_begin" "s1_create_function_in_schema" "s2_drop_schema" "s1_commit"
+permutation "s2_begin" "s2_drop_schema" "s1_create_function_in_schema" "s2_commit"
+
+# alter function - schema
+permutation "s1_begin" "s1_alter_function_schema" "s2_drop_alterschema" "s1_commit"
+permutation "s2_begin" "s2_drop_alterschema" "s1_alter_function_schema" "s2_commit"
+
+# function - argtype
+permutation "s1_begin" "s1_create_function_with_argtype" "s2_drop_foo_type" "s1_commit"
+permutation "s2_begin" "s2_drop_foo_type" "s1_create_function_with_argtype" "s2_commit"
+
+# function - rettype
+permutation "s1_begin" "s1_create_function_with_rettype" "s2_drop_foo_rettype" "s1_commit"
+permutation "s2_begin" "s2_drop_foo_rettype" "s1_create_function_with_rettype" "s2_commit"
+
+# function - function
+permutation "s1_begin" "s1_create_function_with_function" "s2_drop_function_f" "s1_commit"
+permutation "s2_begin" "s2_drop_function_f" "s1_create_function_with_function" "s2_commit"
+
+# domain - domain
+permutation "s1_begin" "s1_create_domain_with_domain" "s2_drop_domain_id" "s1_commit"
+permutation "s2_begin" "s2_drop_domain_id" "s1_create_domain_with_domain" "s2_commit"
+
+# table - type
+permutation "s1_begin" "s1_create_table_with_type" "s2_drop_footab_type" "s1_commit"
+permutation "s2_begin" "s2_drop_footab_type" "s1_create_table_with_type" "s2_commit"
+
+# server - foreign data wrapper
+permutation "s1_begin" "s1_create_server_with_fdw_wrapper" "s2_drop_fdw_wrapper" "s1_commit"
+permutation "s2_begin" "s2_drop_fdw_wrapper" "s1_create_server_with_fdw_wrapper" "s2_commit"
diff --git a/src/test/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9..df8d276dfc 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -37,6 +37,8 @@ NOTICE:  in object access: superuser attempting create (subId=0x0) [internal]
 NOTICE:  in object access: superuser finished create (subId=0x0) [internal]
 NOTICE:  in object access: superuser attempting create (subId=0x0) [internal]
 NOTICE:  in object access: superuser finished create (subId=0x0) [internal]
+NOTICE:  in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
+NOTICE:  in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
 NOTICE:  in process utility: superuser finished CREATE TABLE
 CREATE RULE test_oat_notify AS
   ON UPDATE TO test_oat_schema.test_oat_tab
@@ -62,8 +64,6 @@ BEGIN
   END IF;
 END; $$;
 NOTICE:  in process utility: superuser attempting CREATE FUNCTION
-NOTICE:  in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
-NOTICE:  in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
 NOTICE:  in object access: superuser attempting create (subId=0x0) [explicit]
 NOTICE:  in object access: superuser finished create (subId=0x0) [explicit]
 NOTICE:  in process utility: superuser finished CREATE FUNCTION
diff --git a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
index effdc49145..da6d931994 100644
--- a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
+++ b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
@@ -86,6 +86,8 @@ NOTICE:  in object access: superuser attempting create (subId=0x0) [internal]
 NOTICE:  in object access: superuser finished create (subId=0x0) [internal]
 NOTICE:  in object access: superuser attempting create (subId=0x0) [internal]
 NOTICE:  in object access: superuser finished create (subId=0x0) [internal]
+NOTICE:  in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
+NOTICE:  in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
 NOTICE:  in process utility: superuser finished CREATE TABLE
 CREATE INDEX regress_test_table_t_idx ON regress_test_table (t);
 NOTICE:  in process utility: superuser attempting CREATE INDEX
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 12852aa612..552edf57b9 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2852,11 +2852,12 @@ begin;
 alter table alterlock2
 add constraint alterlock2nv foreign key (f1) references alterlock (f1) NOT VALID;
 select * from my_locks order by 1;
-  relname   |     max_lockmode      
-------------+-----------------------
- alterlock  | ShareRowExclusiveLock
- alterlock2 | ShareRowExclusiveLock
-(2 rows)
+    relname     |     max_lockmode      
+----------------+-----------------------
+ alterlock      | ShareRowExclusiveLock
+ alterlock2     | ShareRowExclusiveLock
+ alterlock_pkey | AccessShareLock
+(3 rows)
 
 commit;
 begin;
-- 
2.34.1


--psqiKZG/Rh65s32s--





^ permalink  raw  reply  [nested|flat] 50+ messages in thread


end of thread, other threads:[~2024-03-29 15:43 UTC | newest]

Thread overview: 50+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2017-03-03 07:39 [PATCH 1/3] Rewrite sections in ddl.sgml related to partitioning amit <[email protected]>
2024-03-29 15:43 [PATCH v18] Avoid orphaned objects dependencies Bertrand Drouvot <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox