Skip to content

Commit b57957e

Browse files
akorotkovCommitfest Bot
authored andcommitted
Implement ALTER TABLE ... MERGE PARTITIONS ... command
This new DDL command merges several partitions into the one partition of the target table. The target partition is created using new createPartitionTable() function with parent partition as the template. This commit comprises quite naive implementation which works in single process and holds the ACCESS EXCLUSIVE LOCK on the parent table during all the operations including the tuple routing. This is why this new DDL command can't be recommended for large partitioned tables under a high load. However, this implementation come in handy in certain cases even as is. Also, it could be used as a foundation for future implementations with lesser locking and possibly parallel. Discussion: https://postgr.es/m/c73a1746-0cd0-6bdd-6b23-3ae0b7c0c582%40postgrespro.ru Author: Dmitry Koval Reviewed-by: Matthias van de Meent, Laurenz Albe, Zhihong Yu, Justin Pryzby Reviewed-by: Alvaro Herrera, Robert Haas, Stephane Tachoires, Jian He Fixes (summary information). Authors: Alexander Korotkov, Tender Wang, Richard Guo, Dagfinn Ilmari Mannsaker Authors: Fujii Masao, Jian He Reviewed-by: Alexander Korotkov, Robert Haas, Justin Pryzby, Pavel Borisov Reviewed-by: Masahiko Sawada Reported-by: Alexander Lakhin, Justin Pryzby, Kyotaro Horiguchi Reported-by: Daniel Gustafsson, Tom Lane, Noah Misch
1 parent 6d12d5a commit b57957e

File tree

21 files changed

+3618
-26
lines changed

21 files changed

+3618
-26
lines changed

doc/src/sgml/ddl.sgml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4450,6 +4450,25 @@ ALTER TABLE measurement_y2006m02 ADD UNIQUE (city_id, logdate);
44504450
ALTER INDEX measurement_city_id_logdate_key
44514451
ATTACH PARTITION measurement_y2006m02_city_id_logdate_key;
44524452
...
4453+
</programlisting>
4454+
</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;
44534472
</programlisting>
44544473
</para>
44554474
</sect3>

doc/src/sgml/ref/alter_table.sgml

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ 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>
4042

4143
<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
4244

@@ -1147,14 +1149,101 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
11471149
</listitem>
11481150
</varlistentry>
11491151

1152+
<varlistentry id="sql-altertable-merge-partitions">
1153+
<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>
1154+
1155+
<listitem>
1156+
<para>
1157+
This form merges several partitions of the target table into a new partition.
1158+
Hash-partitioned target table is not supported.
1159+
If <literal>DEFAULT</literal> partition is not in the
1160+
list of partitions <replaceable class="parameter">partition_name1</replaceable>,
1161+
<replaceable class="parameter">partition_name2</replaceable> [, ...]:
1162+
<itemizedlist>
1163+
<listitem>
1164+
<para>
1165+
For range-partitioned tables, the ranges of the partitions
1166+
<replaceable class="parameter">partition_name1</replaceable>,
1167+
<replaceable class="parameter">partition_name2</replaceable>, [...]
1168+
must be adjacent in order to be merged. Otherwise, an error will be
1169+
raised. The resulting combined range will be the new partition bound
1170+
for the partition <replaceable class="parameter">partition_name</replaceable>.
1171+
</para>
1172+
</listitem>
1173+
<listitem>
1174+
<para>
1175+
For list-partitioned tables, the partition bounds of
1176+
<replaceable class="parameter">partition_name1</replaceable>,
1177+
<replaceable class="parameter">partition_name2</replaceable>, [...]
1178+
are combined to form the new partition bound for
1179+
<replaceable class="parameter">partition_name</replaceable>.
1180+
</para>
1181+
</listitem>
1182+
</itemizedlist>
1183+
If <literal>DEFAULT</literal> partition is in the list of partitions <replaceable class="parameter">partition_name1</replaceable>,
1184+
<replaceable class="parameter">partition_name2</replaceable> [, ...]:
1185+
<itemizedlist>
1186+
<listitem>
1187+
<para>
1188+
The partition <replaceable class="parameter">partition_name</replaceable>
1189+
will be the new <literal>DEFAULT</literal> partition of the target table.
1190+
</para>
1191+
</listitem>
1192+
<listitem>
1193+
<para>
1194+
The partition bound specifications for all partitions-
1195+
<replaceable class="parameter">partition_name1</replaceable>,
1196+
<replaceable class="parameter">partition_name2</replaceable>, [...]
1197+
can be arbitrary.
1198+
</para>
1199+
</listitem>
1200+
</itemizedlist>
1201+
The new partition <replaceable class="parameter">partition_name</replaceable>
1202+
can have the same name as one of the merged partitions. Only simple,
1203+
non-partitioned partitions can be merged.
1204+
</para>
1205+
<para>
1206+
If merged partitions have different owners, an error will be generated.
1207+
The owner of the merged partitions will be the owner of the new partition.
1208+
It is the user's responsibility to setup <acronym>ACL</acronym> on the
1209+
new partition.
1210+
</para>
1211+
<para>
1212+
The indexes and identity are created later, after moving the data
1213+
into the new partition.
1214+
Extended statistics aren't copied from the parent table, for consistency with
1215+
<command>CREATE TABLE PARTITION OF</command>.
1216+
The new partition will inherit the same table access method, persistence
1217+
type, and tablespace as the parent table.
1218+
</para>
1219+
<para>
1220+
When partitions are merged, any individual objects belonging to those
1221+
partitions, such as constraints or statistics will be dropped. This occurs
1222+
because <command>ALTER TABLE MERGE PARTITIONS</command> uses the partitioned table itself as the
1223+
template to define these objects.
1224+
</para>
1225+
<para>
1226+
If merged partitions have some objects dependent on them, the command can
1227+
not be done (<literal>CASCADE</literal> is not used, an error will be returned).
1228+
</para>
1229+
<note>
1230+
<para>
1231+
Merging partitions acquires a <literal>ACCESS EXCLUSIVE</literal> lock on
1232+
the parent table, in addition to the <literal>ACCESS EXCLUSIVE</literal>
1233+
locks on the tables being merged and on the default partition (if any).
1234+
</para>
1235+
</note>
1236+
</listitem>
1237+
</varlistentry>
1238+
11501239
</variablelist>
11511240
</para>
11521241

11531242
<para>
11541243
All the forms of <command>ALTER TABLE</command> that act on a single table,
11551244
except <literal>RENAME</literal>, <literal>SET SCHEMA</literal>,
1156-
<literal>ATTACH PARTITION</literal>, and
1157-
<literal>DETACH PARTITION</literal> can be combined into
1245+
<literal>ATTACH PARTITION</literal>, <literal>DETACH PARTITION</literal>,
1246+
and <literal>MERGE PARTITIONS</literal> can be combined into
11581247
a list of multiple alterations to be applied together. For example, it
11591248
is possible to add several columns and/or alter the type of several
11601249
columns in a single command. This is particularly useful with large
@@ -1397,7 +1486,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
13971486
<term><replaceable class="parameter">partition_name</replaceable></term>
13981487
<listitem>
13991488
<para>
1400-
The name of the table to attach as a new partition or to detach from this table.
1489+
The name of the table to attach as a new partition or to detach from this table,
1490+
or the name of the new merged partition.
1491+
</para>
1492+
</listitem>
1493+
</varlistentry>
1494+
1495+
<varlistentry id="sql-altertable-parms-partition-name1">
1496+
<term><replaceable class="parameter">partition_name1</replaceable></term>
1497+
<term><replaceable class="parameter">partition_name2</replaceable></term>
1498+
<listitem>
1499+
<para>
1500+
The names of the tables being merged into the new partition.
14011501
</para>
14021502
</listitem>
14031503
</varlistentry>
@@ -1830,6 +1930,13 @@ ALTER TABLE measurement
18301930
DETACH PARTITION measurement_y2015m12;
18311931
</programlisting></para>
18321932

1933+
<para>
1934+
To merge several partitions into one partition of the target table:
1935+
<programlisting>
1936+
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_central)
1937+
INTO sales_all;
1938+
</programlisting></para>
1939+
18331940
</refsect1>
18341941

18351942
<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)