Subsequently, we discuss how tables can be organized into
   schemas, and how privileges can be assigned to tables.  Finally,
   we will briefly look at other features that affect the data storage,
-  such as inheritance, views, functions, and triggers.
+  such as inheritance, table partitioning, views, functions, and
+  triggers.
  </para>
 
  <sect1 id="ddl-basics">
    </sect2>
   </sect1>
 
+  <sect1 id="ddl-partitioned-tables">
+   <title>Partitioned Tables</title>
+
+   <indexterm>
+    <primary>partitioned table</primary>
+   </indexterm>
+
+   <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
+    of the <firstterm>partitioning method</firstterm> and a list of columns
+    or expressions to be used as the <firstterm>partition key</firstterm>.
+   </para>
+
+   <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.
+   </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.
+   </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.
+   </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:
+
+    <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>.
+      </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.
+      </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.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       If the partitioned table specified <literal>WITH OIDS</literal> then
+       each partition must also specify <literal>WITH OIDS</literal>. Oids
+       are not automatically inherited by partitions.
+      </para>
+     </listitem>
+
+     <listitem>
+      <para>
+       One cannot drop a <literal>NOT NULL</literal> constraint on a
+       partition's column, if the constraint is present in the parent table.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </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.
+   </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>
+
+     <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>
+
+     <listitem>
+      <para>
+       Since Primary Keys are not supprtable 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>
+       Row triggers, if necessary, must be defined on individual partitions, not
+       the partitioned table as it is currently not supported.
+      </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>
 
      <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</> and <command>DROP TABLE</> are
-      both far faster than a bulk operation.
+      <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>
 
    <para>
     Currently, <productname>PostgreSQL</productname> supports partitioning
-    via table inheritance.  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.
+    using two methods:
+
+    <variablelist>
+     <varlistentry>
+      <term>Using Table 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.  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>
+
+     <varlistentry>
+      <term>Using Partitioned Tables</term>
+
+      <listitem>
+       <para>
+        See last section for some general information:
+        <xref linkend="ddl-partitioned-tables">
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
    </para>
 
    <para>
     The following forms of partitioning can be implemented in
-    <productname>PostgreSQL</productname>:
+    <productname>PostgreSQL</productname> using either of the above mentioned
+    methods, although the latter provides dedicated syntax for each:
 
     <variablelist>
      <varlistentry>
      <title>Implementing Partitioning</title>
 
     <para>
-     To set up a partitioned table, do the following:
+     To set up a partitioned table using inheritance, do the following:
      <orderedlist spacing="compact">
       <listitem>
        <para>
      </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
     <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, partitioning can be set up as follows:
+     steps outlined above for both methods, partitioning can be set up as
+     follows:
     </para>
 
     <para>
      </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);
+</programlisting>
+       </para>
+      </listitem>
+
+      <listitem>
+       <para>
+        Then create partitions as follows:
+
+<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>
+
+      <listitem>
+       <para>
+        Create indexes on the key columns just like in case of inheritance
+        partitions.
+       </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. 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.
+     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>
      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>
+
    <para>
      The simplest option for removing old data is simply to drop the partition
-     that is no longer necessary:
+     that is no longer necessary, which works using both methods of
+     partitioning:
 <programlisting>
 DROP TABLE measurement_y2006m02;
 </programlisting>
 <programlisting>
 ALTER TABLE measurement_y2006m02 NO INHERIT measurement;
 </programlisting>
+
+     When using a partitioned table:
+
+<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
 ) 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
 \copy measurement_y2008m02 from 'measurement_y2008m02'
 -- possibly some other data preparation work
 ALTER TABLE measurement_y2008m02 INHERIT measurement;
+</programlisting>
+
+     The last of the above commands when using a partitioned table would be:
+
+<programlisting>
+ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
+    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );
 </programlisting>
     </para>
+
+    <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>
 
    <sect2 id="ddl-partitioning-constraint-exclusion">
     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">
     <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:
+     master table (unless it is a partitioned table).  For example:
 
 <programlisting>
 CREATE RULE measurement_insert_y2006m02 AS
    <title>Caveats</title>
 
    <para>
-    The following caveats apply to partitioned tables:
+    The following caveats apply to partitioned tables implemented using either
+    method (unless noted otherwise):
    <itemizedlist>
     <listitem>
      <para>
       partitions and creates and/or modifies associated objects than
       to write each by hand.
      </para>
+
+     <para>
+      This is not a problem with partitioned tables though, as trying to
+      create a partition that overlaps with one of the existing partitions
+      results in an error, so it is impossible to end up with partitions
+      that overlap one another.
+     </para>
     </listitem>
 
     <listitem>
       on the partition tables, but it makes management of the structure
       much more complicated.
      </para>
+
+     <para>
+      This problem exists even for partitioned tables.  An <command>UPDATE</>
+      that causes a row to move from one partition to another fails, because
+      the new value of the row fails to satisfy the implicit partition
+      constraint of the original partition.  This might change in future
+      releases.
+     </para>
     </listitem>
 
     <listitem>
 <programlisting>
 ANALYZE measurement;
 </programlisting>
-      will only process the master table.
+      will only process the master table.  This is true even for partitioned
+      tables.
      </para>
     </listitem>
 
       action is only taken in case of unique violations on the specified
       target relation, not its child relations.
      </para>
+
+     <para>
+      <command>INSERT</command> statements with <literal>ON CONFLICT</>
+      clause are currently not allowed on partitioned tables, that is,
+      cause error when specified.
+     </para>
     </listitem>
 
    </itemizedlist>
       range tests for range partitioning, as illustrated in the preceding
       examples.  A good rule of thumb is that partitioning constraints should
       contain only comparisons of the partitioning column(s) to constants
-      using B-tree-indexable operators.
+      using B-tree-indexable operators, which applies even to partitioned
+      tables, because only B-tree-indexable column(s) are allowed in the
+      partition key.
      </para>
     </listitem>
 
       during constraint exclusion, so large numbers of partitions are likely
       to increase query planning time considerably.  Partitioning using
       these techniques will work well with up to perhaps a hundred partitions;
-      don't try to use many thousands of partitions.
+      don't try to use many thousands of partitions. This restriction applies
+      both to inheritance and explicit partitioning syntax.
      </para>
     </listitem>