Skip to content

Commit c42602b

Browse files
author
Commitfest Bot
committed
[CF 3659] v49 - 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 6d12d5a + 99e2dcc commit c42602b

File tree

28 files changed

+8054
-38
lines changed

28 files changed

+8054
-38
lines changed

doc/src/sgml/ddl.sgml

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

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

doc/src/sgml/ref/alter_table.sgml

Lines changed: 199 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ 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+
MERGE PARTITIONS (<replaceable class="parameter">partition_name1</replaceable>, <replaceable class="parameter">partition_name2</replaceable> [, ...]) INTO <replaceable class="parameter">partition_name</replaceable>
42+
ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
43+
SPLIT PARTITION <replaceable class="parameter">partition_name</replaceable> INTO
44+
(PARTITION <replaceable class="parameter">partition_name1</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT },
45+
PARTITION <replaceable class="parameter">partition_name2</replaceable> { FOR VALUES <replaceable class="parameter">partition_bound_spec</replaceable> | DEFAULT } [, ...])
4046

4147
<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
4248

@@ -1147,14 +1153,167 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
11471153
</listitem>
11481154
</varlistentry>
11491155

1156+
<varlistentry id="sql-altertable-split-partition">
1157+
<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>
1158+
1159+
<listitem>
1160+
<para>
1161+
This form splits a single partition of the target table into a new
1162+
partitions. Hash-partitioned target table is not supported. Bounds of new
1163+
partitions should not overlap with new and existing partitions
1164+
(except <replaceable class="parameter">partition_name</replaceable>).
1165+
If the split partition is a <literal>DEFAULT</literal> partition, one of
1166+
the new partitions must be <literal>DEFAULT</literal>.
1167+
In case one of the new partitions or one of existing partitions is
1168+
<literal>DEFAULT</literal>, new partitions <replaceable class="parameter">partition_name1</replaceable>,
1169+
<replaceable class="parameter">partition_name2</replaceable>, ... can
1170+
have spaces between partitions bounds. If the partitioned table does not
1171+
have a <literal>DEFAULT</literal> partition, the <literal>DEFAULT</literal>
1172+
partition can be defined as one of the new partitions.
1173+
</para>
1174+
<para>
1175+
In case new partitions do not contain a <literal>DEFAULT</literal>
1176+
partition and the partitioned table does not have a <literal>DEFAULT</literal>
1177+
partition, the following must be true: sum bounds of new partitions
1178+
<replaceable class="parameter">partition_name1</replaceable>,
1179+
<replaceable class="parameter">partition_name2</replaceable>, ... should
1180+
be equal to bound of split partition <replaceable class="parameter">partition_name</replaceable>.
1181+
One of the new partitions <replaceable class="parameter">partition_name1</replaceable>,
1182+
<replaceable class="parameter">partition_name2</replaceable>, ... can have
1183+
the same name as split partition <replaceable class="parameter">partition_name</replaceable>
1184+
(this is suitable in case of splitting a <literal>DEFAULT</literal>
1185+
partition: we split it, but after splitting we have a partition with the
1186+
same name). Only simple, non-partitioned partition can be split.
1187+
</para>
1188+
<para>
1189+
New partitions will have the same owner as the parent partition.
1190+
It is the user's responsibility to setup <acronym>ACL</acronym> on new
1191+
partitions.
1192+
</para>
1193+
<para>
1194+
The indexes and identity are created later, after moving the data
1195+
into the new partitions.
1196+
Extended statistics aren't copied from the parent table, for consistency with
1197+
<command>CREATE TABLE PARTITION OF</command>.
1198+
New partitions will inherit the same table access method, persistence
1199+
type, and tablespace as the parent table.
1200+
</para>
1201+
<para>
1202+
When partition is split, any individual objects belonging to this
1203+
partition, such as constraints or statistics will be dropped. This ccurs
1204+
because <command>ALTER TABLE SPLIT PARTITION</command> uses the partitioned table itself
1205+
as the template to define these objects.
1206+
</para>
1207+
<para>
1208+
If split partition has some objects dependent on it, the command can
1209+
not be done (<literal>CASCADE</literal> is not used, an error will be returned).
1210+
</para>
1211+
<note>
1212+
<para>
1213+
Split partition acquires a <literal>ACCESS EXCLUSIVE</literal> lock on
1214+
the parent table, in addition to the <literal>ACCESS EXCLUSIVE</literal>
1215+
lock on the table being split.
1216+
</para>
1217+
</note>
1218+
</listitem>
1219+
</varlistentry>
1220+
1221+
<varlistentry id="sql-altertable-merge-partitions">
1222+
<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>
1223+
1224+
<listitem>
1225+
<para>
1226+
This form merges several partitions of the target table into a new partition.
1227+
Hash-partitioned target table is not supported.
1228+
If <literal>DEFAULT</literal> partition is not in the
1229+
list of partitions <replaceable class="parameter">partition_name1</replaceable>,
1230+
<replaceable class="parameter">partition_name2</replaceable> [, ...]:
1231+
<itemizedlist>
1232+
<listitem>
1233+
<para>
1234+
For range-partitioned tables, the ranges of the partitions
1235+
<replaceable class="parameter">partition_name1</replaceable>,
1236+
<replaceable class="parameter">partition_name2</replaceable>, [...]
1237+
must be adjacent in order to be merged. Otherwise, an error will be
1238+
raised. The resulting combined range will be the new partition bound
1239+
for the partition <replaceable class="parameter">partition_name</replaceable>.
1240+
</para>
1241+
</listitem>
1242+
<listitem>
1243+
<para>
1244+
For list-partitioned tables, the partition bounds of
1245+
<replaceable class="parameter">partition_name1</replaceable>,
1246+
<replaceable class="parameter">partition_name2</replaceable>, [...]
1247+
are combined to form the new partition bound for
1248+
<replaceable class="parameter">partition_name</replaceable>.
1249+
</para>
1250+
</listitem>
1251+
</itemizedlist>
1252+
If <literal>DEFAULT</literal> partition is in the list of partitions <replaceable class="parameter">partition_name1</replaceable>,
1253+
<replaceable class="parameter">partition_name2</replaceable> [, ...]:
1254+
<itemizedlist>
1255+
<listitem>
1256+
<para>
1257+
The partition <replaceable class="parameter">partition_name</replaceable>
1258+
will be the new <literal>DEFAULT</literal> partition of the target table.
1259+
</para>
1260+
</listitem>
1261+
<listitem>
1262+
<para>
1263+
The partition bound specifications for all partitions-
1264+
<replaceable class="parameter">partition_name1</replaceable>,
1265+
<replaceable class="parameter">partition_name2</replaceable>, [...]
1266+
can be arbitrary.
1267+
</para>
1268+
</listitem>
1269+
</itemizedlist>
1270+
The new partition <replaceable class="parameter">partition_name</replaceable>
1271+
can have the same name as one of the merged partitions. Only simple,
1272+
non-partitioned partitions can be merged.
1273+
</para>
1274+
<para>
1275+
If merged partitions have different owners, an error will be generated.
1276+
The owner of the merged partitions will be the owner of the new partition.
1277+
It is the user's responsibility to setup <acronym>ACL</acronym> on the
1278+
new partition.
1279+
</para>
1280+
<para>
1281+
The indexes and identity are created later, after moving the data
1282+
into the new partition.
1283+
Extended statistics aren't copied from the parent table, for consistency with
1284+
<command>CREATE TABLE PARTITION OF</command>.
1285+
The new partition will inherit the same table access method, persistence
1286+
type, and tablespace as the parent table.
1287+
</para>
1288+
<para>
1289+
When partitions are merged, any individual objects belonging to those
1290+
partitions, such as constraints or statistics will be dropped. This occurs
1291+
because <command>ALTER TABLE MERGE PARTITIONS</command> uses the partitioned table itself as the
1292+
template to define these objects.
1293+
</para>
1294+
<para>
1295+
If merged partitions have some objects dependent on them, the command can
1296+
not be done (<literal>CASCADE</literal> is not used, an error will be returned).
1297+
</para>
1298+
<note>
1299+
<para>
1300+
Merging partitions acquires a <literal>ACCESS EXCLUSIVE</literal> lock on
1301+
the parent table, in addition to the <literal>ACCESS EXCLUSIVE</literal>
1302+
locks on the tables being merged and on the default partition (if any).
1303+
</para>
1304+
</note>
1305+
</listitem>
1306+
</varlistentry>
1307+
11501308
</variablelist>
11511309
</para>
11521310

11531311
<para>
11541312
All the forms of <command>ALTER TABLE</command> that act on a single table,
11551313
except <literal>RENAME</literal>, <literal>SET SCHEMA</literal>,
1156-
<literal>ATTACH PARTITION</literal>, and
1157-
<literal>DETACH PARTITION</literal> can be combined into
1314+
<literal>ATTACH PARTITION</literal>, <literal>DETACH PARTITION</literal>,
1315+
<literal>SPLIT PARTITION</literal>, and <literal>MERGE PARTITIONS</literal>
1316+
can be combined into
11581317
a list of multiple alterations to be applied together. For example, it
11591318
is possible to add several columns and/or alter the type of several
11601319
columns in a single command. This is particularly useful with large
@@ -1397,7 +1556,19 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
13971556
<term><replaceable class="parameter">partition_name</replaceable></term>
13981557
<listitem>
13991558
<para>
1400-
The name of the table to attach as a new partition or to detach from this table.
1559+
The name of the table to attach as a new partition or to detach from this table,
1560+
or the name of split partition, or the name of the new merged partition.
1561+
</para>
1562+
</listitem>
1563+
</varlistentry>
1564+
1565+
<varlistentry id="sql-altertable-parms-partition-name1">
1566+
<term><replaceable class="parameter">partition_name1</replaceable></term>
1567+
<term><replaceable class="parameter">partition_name2</replaceable></term>
1568+
<listitem>
1569+
<para>
1570+
The names of the tables being merged into the new partition or split into
1571+
new partitions.
14011572
</para>
14021573
</listitem>
14031574
</varlistentry>
@@ -1830,6 +2001,31 @@ ALTER TABLE measurement
18302001
DETACH PARTITION measurement_y2015m12;
18312002
</programlisting></para>
18322003

2004+
<para>
2005+
To split a single partition of the range-partitioned table:
2006+
<programlisting>
2007+
ALTER TABLE sales_range SPLIT PARTITION sales_feb_mar_apr2023 INTO
2008+
(PARTITION sales_feb2023 FOR VALUES FROM ('2023-02-01') TO ('2023-03-01'),
2009+
PARTITION sales_mar2023 FOR VALUES FROM ('2023-03-01') TO ('2023-04-01'),
2010+
PARTITION sales_apr2023 FOR VALUES FROM ('2023-04-01') TO ('2023-05-01'));
2011+
</programlisting></para>
2012+
2013+
<para>
2014+
To split a single partition of the list-partitioned table:
2015+
<programlisting>
2016+
ALTER TABLE sales_list SPLIT PARTITION sales_all INTO
2017+
(PARTITION sales_west FOR VALUES IN ('Lisbon', 'New York', 'Madrid'),
2018+
PARTITION sales_east FOR VALUES IN ('Bejing', 'Delhi', 'Vladivostok'),
2019+
PARTITION sales_central FOR VALUES IN ('Warsaw', 'Berlin', 'Kyiv'));
2020+
</programlisting></para>
2021+
2022+
<para>
2023+
To merge several partitions into one partition of the target table:
2024+
<programlisting>
2025+
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_central)
2026+
INTO sales_all;
2027+
</programlisting></para>
2028+
18332029
</refsect1>
18342030

18352031
<refsect1>

src/backend/catalog/dependency.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,56 @@ performDeletion(const ObjectAddress *object,
319319
table_close(depRel, RowExclusiveLock);
320320
}
321321

322+
/*
323+
* performDeletionCheck: Check whether a specific object can be safely deleted.
324+
* This function does not perform any deletion; instead, it raises an error
325+
* if the object cannot be deleted due to existing dependencies.
326+
*
327+
* It can be useful when you need delete some objects later. See comments in
328+
* performDeletion too.
329+
* The behavior must specified as DROP_RESTRICT.
330+
*/
331+
void
332+
performDeletionCheck(const ObjectAddress *object,
333+
DropBehavior behavior, int flags)
334+
{
335+
Relation depRel;
336+
ObjectAddresses *targetObjects;
337+
338+
Assert(behavior == DROP_RESTRICT);
339+
340+
depRel = table_open(DependRelationId, RowExclusiveLock);
341+
342+
AcquireDeletionLock(object, 0);
343+
344+
/*
345+
* Construct a list of objects we want delete later (ie, the given object plus
346+
* everything directly or indirectly dependent on it).
347+
*/
348+
targetObjects = new_object_addresses();
349+
350+
findDependentObjects(object,
351+
DEPFLAG_ORIGINAL,
352+
flags,
353+
NULL, /* empty stack */
354+
targetObjects,
355+
NULL, /* no pendingObjects */
356+
&depRel);
357+
358+
/*
359+
* Check if deletion is allowed.
360+
*/
361+
reportDependentObjects(targetObjects,
362+
behavior,
363+
flags,
364+
object);
365+
366+
/* And clean up */
367+
free_object_addresses(targetObjects);
368+
369+
table_close(depRel, RowExclusiveLock);
370+
}
371+
322372
/*
323373
* performMultipleDeletions: Similar to performDeletion, but act on multiple
324374
* objects at once.

src/backend/catalog/heap.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,11 @@ static ObjectAddress AddNewRelationType(const char *typeName,
102102
Oid new_row_type,
103103
Oid new_array_type);
104104
static void RelationRemoveInheritance(Oid relid);
105+
static void StoreConstraints(Relation rel, List *cooked_constraints,
106+
bool is_internal);
105107
static Oid StoreRelCheck(Relation rel, const char *ccname, Node *expr,
106108
bool is_enforced, bool is_validated, bool is_local,
107109
int16 inhcount, bool is_no_inherit, bool is_internal);
108-
static void StoreConstraints(Relation rel, List *cooked_constraints,
109-
bool is_internal);
110110
static bool MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
111111
bool allow_merge, bool is_local,
112112
bool is_enforced,

src/backend/catalog/pg_constraint.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
875875
false)));
876876
constr->is_enforced = true;
877877
constr->skip_validation = !conForm->convalidated;
878-
constr->initially_valid = true;
878+
constr->initially_valid = conForm->convalidated;
879879
constr->is_no_inherit = conForm->connoinherit;
880880
notnulls = lappend(notnulls, constr);
881881
}

0 commit comments

Comments
 (0)