diff options
| author | Alexander Korotkov | 2025-12-14 11:29:38 +0000 |
|---|---|---|
| committer | Alexander Korotkov | 2025-12-14 11:29:38 +0000 |
| commit | 4b3d173629f4cd7ab6cd700d1053af5d5c7c9e37 (patch) | |
| tree | 79d024319e181b99718b9725d5e472b069e7414c /doc/src | |
| parent | f2e4cc427951b7c46629fb7625a22f7898586f3a (diff) | |
Implement ALTER TABLE ... SPLIT PARTITION ... command
This new DDL command splits a single partition into several partitions. Just
like the ALTER TABLE ... MERGE PARTITIONS ... command, new partitions are
created using the createPartitionTable() function with the parent partition
as the template.
This commit comprises a quite naive implementation which works in a single
process and holds the ACCESS EXCLUSIVE LOCK on the parent table during all
the operations, including the tuple routing. This is why the new DDL command
can't be recommended for large, partitioned tables under high load. However,
this implementation comes in handy in certain cases, even as it is. Also, it
could serve as a foundation for future implementations with less locking and
possibly parallelism.
Discussion: https://postgr.es/m/c73a1746-0cd0-6bdd-6b23-3ae0b7c0c582%40postgrespro.ru
Author: Dmitry Koval <d.koval@postgrespro.ru>
Co-authored-by: Alexander Korotkov <aekorotkov@gmail.com>
Co-authored-by: Tender Wang <tndrwang@gmail.com>
Co-authored-by: Richard Guo <guofenglinux@gmail.com>
Co-authored-by: Dagfinn Ilmari Mannsaker <ilmari@ilmari.org>
Co-authored-by: Fujii Masao <masao.fujii@gmail.com>
Co-authored-by: Jian He <jian.universality@gmail.com>
Reviewed-by: Matthias van de Meent <boekewurm+postgres@gmail.com>
Reviewed-by: Laurenz Albe <laurenz.albe@cybertec.at>
Reviewed-by: Zhihong Yu <zyu@yugabyte.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Alvaro Herrera <alvherre@alvh.no-ip.org>
Reviewed-by: Robert Haas <rhaas@postgresql.org>
Reviewed-by: Stephane Tachoires <stephane.tachoires@gmail.com>
Reviewed-by: Jian He <jian.universality@gmail.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
Reviewed-by: Pavel Borisov <pashkin.elfe@gmail.com>
Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-by: Alexander Lakhin <exclusion@gmail.com>
Reviewed-by: Kyotaro Horiguchi <horikyota.ntt@gmail.com>
Reviewed-by: Daniel Gustafsson <dgustafsson@postgresql.org>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Noah Misch <noah@leadboat.com>
Diffstat (limited to 'doc/src')
| -rw-r--r-- | doc/src/sgml/ddl.sgml | 19 | ||||
| -rw-r--r-- | doc/src/sgml/ref/alter_table.sgml | 118 |
2 files changed, 134 insertions, 3 deletions
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index 7b90789f87c..cea28c00f8a 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -4764,6 +4764,25 @@ ALTER TABLE measurement measurement_y2006m03) INTO measurement_y2006q1; </programlisting> </para> + + <para> + Similarly to merging multiple table partitions, there is an option for + splitting a single partition into multiple using the + <link linkend="sql-altertable-split-partition"><command>ALTER TABLE ... SPLIT PARTITION</command></link>. + This feature could come in handy when one partition grows too big + and needs to be split into multiple. It's important to note that + this operation is not supported for hash-partitioned tables and acquires + an <literal>ACCESS EXCLUSIVE</literal> lock, which could impact high-load + systems due to the lock's restrictive nature. For example, we can split + the quarter partition back to monthly partitions: +<programlisting> +ALTER TABLE measurement SPLIT PARTITION measurement_y2006q1 INTO + (PARTITION measurement_y2006m01 FOR VALUES FROM ('2006-01-01') TO ('2006-02-01'), + PARTITION measurement_y2006m02 FOR VALUES FROM ('2006-02-01') TO ('2006-03-01'), + PARTITION measurement_y2006m03 FOR VALUES FROM ('2006-03-01') TO ('2006-04-01')); +</programlisting> + </para> + </sect3> <sect3 id="ddl-partitioning-declarative-limitations"> diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 5cda1c94adb..9abd8037f28 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -39,6 +39,10 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> DETACH PARTITION <replaceable class="parameter">partition_name</replaceable> [ CONCURRENTLY | FINALIZE ] ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> MERGE PARTITIONS (<replaceable class="parameter">partition_name1</replaceable>, <replaceable class="parameter">partition_name2</replaceable> [, ...]) INTO <replaceable class="parameter">partition_name</replaceable> +ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> + SPLIT PARTITION <replaceable class="parameter">partition_name</replaceable> INTO + (PARTITION <replaceable class="parameter">partition_name1</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT }, + PARTITION <replaceable class="parameter">partition_name2</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT } [, ...]) <phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase> @@ -1258,6 +1262,94 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM </listitem> </varlistentry> + <varlistentry id="sql-altertable-split-partition"> + <term> + <literal>SPLIT PARTITION <replaceable class="parameter">partition_name</replaceable> INTO ( + PARTITION <replaceable class="parameter">partition_name1</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT }, + PARTITION <replaceable class="parameter">partition_name2</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT } + [, ...])</literal> + </term> + + <listitem> + <para> + This form splits a single partition of the target table into new + partitions. Hash-partitioned target table is not supported. + Only a simple, non-partitioned partition can be split. + If the split partition is the <literal>DEFAULT</literal> partition, + one of the new partitions must be <literal>DEFAULT</literal>. + If the partitioned table does not have a <literal>DEFAULT</literal> + partition, a <literal>DEFAULT</literal> partition can be defined as one + of the new partitions. + </para> + + <para> + The bounds of new partitions should not overlap with those of new or + existing partitions (except <replaceable class="parameter">partition_name</replaceable>). + The combined bounds of new partitions <literal> + <replaceable class="parameter">partition_name1</replaceable>, + <replaceable class="parameter">partition_name2</replaceable>[, ...] + </literal> should be equal to the bounds of the split partition + <replaceable class="parameter">partition_name</replaceable>. + One of the new partitions can have the same name as the split partition + <replaceable class="parameter">partition_name</replaceable> + (this is suitable in case of splitting the <literal>DEFAULT</literal> + partition: after the split, the <literal>DEFAULT</literal> partition + remains with the same name, but its partition bound changes). + </para> + + <para> + New partitions will have the same owner as the parent partition. + It is the user's responsibility to setup <acronym>ACL</acronym> on new + partitions. + </para> + + <para> + <command>ALTER TABLE SPLIT PARTITION</command> uses the partitioned + table itself as the template to construct new partitions. + New partitions will inherit the same table access method, persistence + type, and tablespace as the partitioned table. + </para> + + <para> + Constraints, column defaults, column generation expressions, + identity columns, indexes, and triggers are copied from the partitioned + table to the new partitions. But extended statistics, security + policies, etc, won't be copied from the partitioned table. + Indexes and identity columns copied from the partitioned table will be + created afterward, once the data has been moved into the new partitions. + </para> + + <para> + When a partition is split, any objects that depend on this partition, + such as constraints, triggers, extended statistics, etc, will be dropped. + This occurs because <command>ALTER TABLE SPLIT PARTITION</command> uses + the partitioned table itself as the template to reconstruct these + objects later. + Eventually, we will drop the split partition + (using <literal>RESTRICT</literal> mode) too; therefore, if any objects + are still dependent on it, <command>ALTER TABLE SPLIT PARTITION</command> + would fail (see <xref linkend="ddl-depend"/>). + </para> + + <note> + <para> + Split partition acquires an <literal>ACCESS EXCLUSIVE</literal> lock on + the parent table, in addition to the <literal>ACCESS EXCLUSIVE</literal> + lock on the table being split. + </para> + </note> + <note> + + <para> + <command>ALTER TABLE SPLIT PARTITION</command> creates new partitions and + moves data from the split partition into them, which can take a long + time. So it is not recommended to use the command for splitting a + small fraction of rows out of a very big partition. + </para> + </note> + </listitem> + </varlistentry> + </variablelist> </para> @@ -1265,7 +1357,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM All the forms of <command>ALTER TABLE</command> that act on a single table, except <literal>RENAME</literal>, <literal>SET SCHEMA</literal>, <literal>ATTACH PARTITION</literal>, <literal>DETACH PARTITION</literal>, - and <literal>MERGE PARTITIONS</literal>, can be combined into + <literal>MERGE PARTITIONS</literal>, and <literal>SPLIT PARTITION</literal> + can be combined into a list of multiple alterations to be applied together. For example, it is possible to add several columns and/or alter the type of several columns in a single command. This is particularly useful with large @@ -1509,7 +1602,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM <listitem> <para> The name of the table to attach as a new partition or to detach from this table, - or the name of the new merged partition. + or the name of split partition, or the name of the new merged partition. </para> </listitem> </varlistentry> @@ -1519,7 +1612,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM <term><replaceable class="parameter">partition_name2</replaceable></term> <listitem> <para> - The names of the tables being merged into the new partition. + The names of the tables being merged into the new partition or split into + new partitions. </para> </listitem> </varlistentry> @@ -1953,6 +2047,24 @@ ALTER TABLE measurement </programlisting></para> <para> + To split a single partition of the range-partitioned table: +<programlisting> +ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2023 INTO + (PARTITION sales_feb2023 FOR VALUES FROM ('2023-02-01') TO ('2023-03-01'), + PARTITION sales_mar2023 FOR VALUES FROM ('2023-03-01') TO ('2023-04-01'), + PARTITION sales_apr2023 FOR VALUES FROM ('2023-04-01') TO ('2023-05-01')); +</programlisting></para> + + <para> + To split a single partition of the list-partitioned table: +<programlisting> +ALTER TABLE sales_list SPLIT PARTITION sales_all INTO + (PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'), + PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'), + PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv')); +</programlisting></para> + + <para> To merge several partitions into one partition of the target table: <programlisting> ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_central) |
