Skip to content

Commit 2a19c26

Browse files
author
Commitfest Bot
committed
[CF 3659] v38 - Add SPLIT PARTITION/MERGE PARTITIONS commands
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/3659 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/[email protected] Author(s): Dmitry Koval
2 parents 29f7ce6 + 9f42ff4 commit 2a19c26

File tree

26 files changed

+7353
-39
lines changed

26 files changed

+7353
-39
lines changed

doc/src/sgml/ddl.sgml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4441,6 +4441,44 @@ ALTER INDEX measurement_city_id_logdate_key
44414441
...
44424442
</programlisting>
44434443
</para>
4444+
4445+
<para>
4446+
There is also an option for merging multiple table partitions into
4447+
a single partition using the
4448+
<link linkend="sql-altertable-merge-partitions"><command>ALTER TABLE ... MERGE PARTITIONS</command></link>.
4449+
This feature simplifies the management of partitioned tables by allowing
4450+
users to combine partitions that are no longer needed as
4451+
separate entities. It's important to note that this operation is not
4452+
supported for hash-partitioned tables and acquires an
4453+
<literal>ACCESS EXCLUSIVE</literal> lock, which could impact high-load
4454+
systems due to the lock's restrictive nature. For example, we can
4455+
merge three monthly partitions into one quarter partition:
4456+
<programlisting>
4457+
ALTER TABLE measurement
4458+
MERGE PARTITIONS (measurement_y2006m01,
4459+
measurement_y2006m02,
4460+
measurement_y2006m03) INTO measurement_y2006q1;
4461+
</programlisting>
4462+
</para>
4463+
4464+
<para>
4465+
Similarly to merging multiple table partitions, there is an option for
4466+
splitting a single partition into multiple using the
4467+
<link linkend="sql-altertable-split-partition"><command>ALTER TABLE ... SPLIT PARTITION</command></link>.
4468+
This feature could come in handy when one partition grows too big
4469+
and needs to be split into multiple. It's important to note that
4470+
this operation is not supported for hash-partitioned tables and acquires
4471+
an <literal>ACCESS EXCLUSIVE</literal> lock, which could impact high-load
4472+
systems due to the lock's restrictive nature. For example, we can split
4473+
the quarter partition back to monthly partitions:
4474+
<programlisting>
4475+
ALTER TABLE measurement SPLIT PARTITION measurement_y2006q1 INTO
4476+
(PARTITION measurement_y2006m01 FOR VALUES FROM ('2006-01-01') TO ('2006-02-01'),
4477+
PARTITION measurement_y2006m02 FOR VALUES FROM ('2006-02-01') TO ('2006-03-01'),
4478+
PARTITION measurement_y2006m03 FOR VALUES FROM ('2006-03-01') TO ('2006-04-01'));
4479+
</programlisting>
4480+
</para>
4481+
44444482
</sect3>
44454483

44464484
<sect3 id="ddl-partitioning-declarative-limitations">

doc/src/sgml/ref/alter_table.sgml

Lines changed: 164 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
3737
ATTACH PARTITION <replaceable class="parameter">partition_name</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT }
3838
ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
3939
DETACH PARTITION <replaceable class="parameter">partition_name</replaceable> [ CONCURRENTLY | FINALIZE ]
40+
ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
41+
SPLIT PARTITION <replaceable class="parameter">partition_name</replaceable> INTO
42+
(PARTITION <replaceable class="parameter">partition_name1</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT },
43+
PARTITION <replaceable class="parameter">partition_name2</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT } [, ...])
44+
ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
45+
MERGE PARTITIONS (<replaceable class="parameter">partition_name1</replaceable>, <replaceable class="parameter">partition_name2</replaceable> [, ...])
46+
INTO <replaceable class="parameter">partition_name</replaceable>
4047

4148
<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
4249

@@ -1147,14 +1154,142 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
11471154
</listitem>
11481155
</varlistentry>
11491156

1157+
<varlistentry id="sql-altertable-split-partition">
1158+
<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>
1159+
1160+
<listitem>
1161+
<para>
1162+
This form splits a single partition of the target table. Hash-partitioning
1163+
is not supported. Bounds of new partitions should not overlap with new and
1164+
existing partitions (except <replaceable class="parameter">partition_name</replaceable>).
1165+
If the split partition is a DEFAULT partition, one of the new partitions must be DEFAULT.
1166+
In case one of the new partitions or one of existing partitions is DEFAULT,
1167+
new partitions <replaceable class="parameter">partition_name1</replaceable>,
1168+
<replaceable class="parameter">partition_name2</replaceable>, ... can have spaces
1169+
between partitions bounds. If the partitioned table does not have a DEFAULT
1170+
partition, the DEFAULT partition can be defined as one of the new partitions.
1171+
</para>
1172+
<para>
1173+
In case new partitions do not contain a DEFAULT partition and the partitioned table
1174+
does not have a DEFAULT partition, the following must be true: sum bounds of
1175+
new partitions <replaceable class="parameter">partition_name1</replaceable>,
1176+
<replaceable class="parameter">partition_name2</replaceable>, ... should be
1177+
equal to bound of split partition <replaceable class="parameter">partition_name</replaceable>.
1178+
One of the new partitions <replaceable class="parameter">partition_name1</replaceable>,
1179+
<replaceable class="parameter">partition_name2</replaceable>, ... can have
1180+
the same name as split partition <replaceable class="parameter">partition_name</replaceable>
1181+
(this is suitable in case of splitting a DEFAULT partition: we split it, but after
1182+
splitting we have a partition with the same name).
1183+
Only simple, non-partitioned partition can be split.
1184+
</para>
1185+
<para>
1186+
New partitions will have the same owner as the parent.
1187+
</para>
1188+
<para>
1189+
The indexes and identity are created later, after moving the data
1190+
into the new partitions.
1191+
Extended statistics aren't copied from the parent table, for consistency with
1192+
<command>CREATE TABLE PARTITION OF</command>.
1193+
1194+
New partitions will have the same table access method as the parent.
1195+
If the parent table is persistent then new partitions are created
1196+
persistent. If the parent table is temporary then new partitions
1197+
are also created temporary. New partitions will also be created in
1198+
the same tablespace as the parent.
1199+
</para>
1200+
<note>
1201+
<para>
1202+
This command acquires an <literal>ACCESS EXCLUSIVE</literal> lock.
1203+
This is a significant limitation, which limits the usage of this
1204+
command with large partitioned tables under a high load.
1205+
</para>
1206+
</note>
1207+
</listitem>
1208+
</varlistentry>
1209+
1210+
<varlistentry id="sql-altertable-merge-partitions">
1211+
<term><literal>MERGE PARTITIONS (<replaceable class="parameter">partition_name1</replaceable>, <replaceable class="parameter">partition_name2</replaceable> [, ...]) INTO <replaceable class="parameter">partition_name</replaceable></literal></term>
1212+
1213+
<listitem>
1214+
<para>
1215+
This form merges several partitions into the one partition of the target table.
1216+
Hash-partitioning is not supported. If DEFAULT partition is not in the
1217+
list of partitions <replaceable class="parameter">partition_name1</replaceable>,
1218+
<replaceable class="parameter">partition_name2</replaceable> [, ...]:
1219+
<itemizedlist>
1220+
<listitem>
1221+
<para>
1222+
For range-partitioned tables it is necessary that the ranges
1223+
of the partitions <replaceable class="parameter">partition_name1</replaceable>,
1224+
<replaceable class="parameter">partition_name2</replaceable> [, ...] can
1225+
be merged into one range without spaces and overlaps (otherwise an error
1226+
will be generated). The combined range will be the range for the partition
1227+
<replaceable class="parameter">partition_name</replaceable>.
1228+
</para>
1229+
</listitem>
1230+
<listitem>
1231+
<para>
1232+
For list-partitioned tables the value lists of all partitions
1233+
<replaceable class="parameter">partition_name1</replaceable>,
1234+
<replaceable class="parameter">partition_name2</replaceable> [, ...] are
1235+
combined and form the list of values of partition
1236+
<replaceable class="parameter">partition_name</replaceable>.
1237+
</para>
1238+
</listitem>
1239+
</itemizedlist>
1240+
If DEFAULT partition is in the list of partitions <replaceable class="parameter">partition_name1</replaceable>,
1241+
<replaceable class="parameter">partition_name2</replaceable> [, ...]:
1242+
<itemizedlist>
1243+
<listitem>
1244+
<para>
1245+
The partition <replaceable class="parameter">partition_name</replaceable>
1246+
will be the DEFAULT partition.
1247+
</para>
1248+
</listitem>
1249+
<listitem>
1250+
<para>
1251+
For range- and list-partitioned tables the ranges and lists of values
1252+
of the merged partitions can be any.
1253+
</para>
1254+
</listitem>
1255+
</itemizedlist>
1256+
The new partition <replaceable class="parameter">partition_name</replaceable>
1257+
can have the same name as one of the merged partitions. Only simple,
1258+
non-partitioned partitions can be merged.
1259+
</para>
1260+
<para>
1261+
If merged partitions have different owners, an error will be generated.
1262+
</para>
1263+
<para>
1264+
The indexes and identity are created later, after moving the data
1265+
into the new partition.
1266+
Extended statistics aren't copied from the parent table, for consistency with
1267+
<command>CREATE TABLE PARTITION OF</command>.
1268+
The new partition will have the same table access method as the parent.
1269+
If the parent table is persistent then the new partition is created
1270+
persistent. If the parent table is temporary then the new partition
1271+
is also created temporary. The new partition will also be created in
1272+
the same tablespace as the parent.
1273+
</para>
1274+
<note>
1275+
<para>
1276+
This command acquires an <literal>ACCESS EXCLUSIVE</literal> lock.
1277+
This is a significant limitation, which limits the usage of this
1278+
command with large partitioned tables under a high load.
1279+
</para>
1280+
</note>
1281+
</listitem>
1282+
</varlistentry>
1283+
11501284
</variablelist>
11511285
</para>
11521286

11531287
<para>
11541288
All the forms of <command>ALTER TABLE</command> that act on a single table,
11551289
except <literal>RENAME</literal>, <literal>SET SCHEMA</literal>,
1156-
<literal>ATTACH PARTITION</literal>, and
1157-
<literal>DETACH PARTITION</literal> can be combined into
1290+
<literal>ATTACH PARTITION</literal>, <literal>DETACH PARTITION</literal>,
1291+
<literal>SPLIT PARTITION</literal>, and <literal>MERGE PARTITIONS</literal>
1292+
can be combined into
11581293
a list of multiple alterations to be applied together. For example, it
11591294
is possible to add several columns and/or alter the type of several
11601295
columns in a single command. This is particularly useful with large
@@ -1397,7 +1532,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
13971532
<term><replaceable class="parameter">partition_name</replaceable></term>
13981533
<listitem>
13991534
<para>
1400-
The name of the table to attach as a new partition or to detach from this table.
1535+
The name of the table to attach as a new partition or to detach from this table,
1536+
or the name of split partition, or the name of the new merged partition.
14011537
</para>
14021538
</listitem>
14031539
</varlistentry>
@@ -1830,6 +1966,31 @@ ALTER TABLE measurement
18301966
DETACH PARTITION measurement_y2015m12;
18311967
</programlisting></para>
18321968

1969+
<para>
1970+
To split a single partition of the range-partitioned table:
1971+
<programlisting>
1972+
ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2023 INTO
1973+
(PARTITION sales_feb2023 FOR VALUES FROM ('2023-02-01') TO ('2023-03-01'),
1974+
PARTITION sales_mar2023 FOR VALUES FROM ('2023-03-01') TO ('2023-04-01'),
1975+
PARTITION sales_apr2023 FOR VALUES FROM ('2023-04-01') TO ('2023-05-01'));
1976+
</programlisting></para>
1977+
1978+
<para>
1979+
To split a single partition of the list-partitioned table:
1980+
<programlisting>
1981+
ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
1982+
(PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
1983+
PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'),
1984+
PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
1985+
</programlisting></para>
1986+
1987+
<para>
1988+
To merge several partitions into one partition of the target table:
1989+
<programlisting>
1990+
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_central)
1991+
INTO sales_all;
1992+
</programlisting></para>
1993+
18331994
</refsect1>
18341995

18351996
<refsect1>

src/backend/catalog/heap.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,6 @@ static void RelationRemoveInheritance(Oid relid);
105105
static Oid StoreRelCheck(Relation rel, const char *ccname, Node *expr,
106106
bool is_enforced, bool is_validated, bool is_local,
107107
int16 inhcount, bool is_no_inherit, bool is_internal);
108-
static void StoreConstraints(Relation rel, List *cooked_constraints,
109-
bool is_internal);
110108
static bool MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
111109
bool allow_merge, bool is_local,
112110
bool is_enforced,
@@ -2296,7 +2294,7 @@ StoreRelNotNull(Relation rel, const char *nnname, AttrNumber attnum,
22962294
* expressions can be added later, by direct calls to StoreAttrDefault
22972295
* and StoreRelCheck (see AddRelationNewConstraints()).
22982296
*/
2299-
static void
2297+
void
23002298
StoreConstraints(Relation rel, List *cooked_constraints, bool is_internal)
23012299
{
23022300
int numchecks = 0;

0 commit comments

Comments
 (0)