original partition.
</para>
<para>
Partitions may themselves be defined as partitioned tables, resulting
in <firstterm>sub-partitioning</firstterm>. Although all partitions
must have the same columns as their partitioned parent, partitions may
have their
own indexes, constraints and default values, distinct from those of other
partitions. See <xref linkend="sql-createtable"/> for more details on
creating partitioned tables and partitions.
</para>
<para>
It is not possible to turn a regular table into a partitioned table or
vice versa. However, it is possible to add an existing regular or
partitioned table as a partition of a partitioned table, or remove a
partition from a partitioned table turning it into a standalone table;
this can simplify and speed up many maintenance processes.
See <xref linkend="sql-altertable"/> to learn more about the
<command>ATTACH PARTITION</command> and <command>DETACH PARTITION</command>
sub-commands.
</para>
<para>
Partitions can also be <link linkend="ddl-foreign-data">foreign
tables</link>, although considerable care is needed because it is then
the user's responsibility that the contents of the foreign table
satisfy the partitioning rule. There are some other restrictions as
well. See <xref linkend="sql-createforeigntable"/> for more
information.
</para>
<sect3 id="ddl-partitioning-declarative-example">
<title>Example</title>
<para>
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 keep only 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>
Create the <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>
Create partitions. Each partition's definition must specify bounds
that correspond to the partitioning method and partition key of the
parent. Note that specifying bounds such that the new partition's
values would overlap with those in one or more existing partitions will
cause an error.
</para>
<para>
Partitions thus created are in every way normal
<productname>PostgreSQL</productname>
tables (or, possibly, foreign tables). It is possible to specify a
tablespace and storage parameters for each partition separately.
</para>
<para>
For our example, each partition should hold one month's worth of
data, to match the requirement of deleting one month's data at a
time. So the commands might look like:
<programlisting>
CREATE TABLE