the system does not try to check
whether that's really the case.
</para>
<para>
Inserting data into the parent table that does not map
to one of the existing partitions will cause an error; an appropriate
partition must be added manually.
</para>
<para>
It is not necessary to manually create table constraints describing
the partition boundary conditions for partitions. Such constraints
will be created automatically.
</para>
</listitem>
<listitem>
<para>
Create an index on the key column(s), as well as any other indexes you
might want, on the partitioned table. (The key index is not strictly
necessary, but in most scenarios it is helpful.)
This automatically creates a matching index on each partition, and
any partitions you create or attach later will also have such an
index.
An index or unique constraint declared on a partitioned table
is <quote>virtual</quote> in the same way that the partitioned table
is: the actual data is in child indexes on the individual partition
tables.
<programlisting>
CREATE INDEX ON measurement (logdate);
</programlisting>
</para>
</listitem>
<listitem>
<para>
Ensure that the <xref linkend="guc-enable-partition-pruning"/>
configuration parameter is not disabled in <filename>postgresql.conf</filename>.
If it is, queries will not be optimized as desired.
</para>
</listitem>
</orderedlist>
</para>
<para>
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>
<sect3 id="ddl-partitioning-declarative-maintenance">
<title>Partition Maintenance</title>
<para>
Normally the set of partitions established when initially defining the
table is not intended to remain static. It is common to want to
remove partitions holding old 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>
The simplest option for removing old data is 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>
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. This has two forms:
<programlisting>
ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;
ALTER TABLE measurement DETACH PARTITION measurement_y2006m02 CONCURRENTLY;
</programlisting>
These allow 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</command>, <application>pg_dump</application>, or
similar tools. It might also be a useful time to aggregate data
into smaller formats, perform other data manipulations, or run
reports. The first form of the command requires an
<literal>ACCESS EXCLUSIVE</literal> lock on the parent table.
Adding the <literal>CONCURRENTLY</literal> qualifier as in the second
form allows the detach operation to require only
<literal>SHARE UPDATE EXCLUSIVE</literal> lock on the parent table, but see
<link linkend="sql-altertable-detach-partition"><literal>ALTER