Skip to content

Commit c99ef18

Browse files
committed
Checks for ALTER TABLE ... SPLIT/MERGE PARTITIONS ... commands
Check that the target partition actually belongs to the parent table. Reported-by: Alexander Lakhin Discussion: https://postgr.es/m/cd842601-cf1a-9806-f7b7-d2509b93ba61%40gmail.com Author: Dmitry Koval
1 parent b1b13d2 commit c99ef18

File tree

6 files changed

+111
-21
lines changed

6 files changed

+111
-21
lines changed

src/backend/commands/tablecmds.c

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21223,12 +21223,6 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
2122321223
*/
2122421224
splitRel = table_openrv(cmd->name, AccessExclusiveLock);
2122521225

21226-
if (splitRel->rd_rel->relkind != RELKIND_RELATION)
21227-
ereport(ERROR,
21228-
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
21229-
errmsg("cannot split non-table partition \"%s\"",
21230-
RelationGetRelationName(splitRel))));
21231-
2123221226
splitRelOid = RelationGetRelid(splitRel);
2123321227

2123421228
/* Check descriptions of new partitions. */
@@ -21463,12 +21457,6 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
2146321457
*/
2146421458
mergingPartition = table_openrv(name, AccessExclusiveLock);
2146521459

21466-
if (mergingPartition->rd_rel->relkind != RELKIND_RELATION)
21467-
ereport(ERROR,
21468-
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
21469-
errmsg("cannot merge non-table partition \"%s\"",
21470-
RelationGetRelationName(mergingPartition))));
21471-
2147221460
/*
2147321461
* Checking that two partitions have the same name was before, in
2147421462
* function transformPartitionCmdForMerge().

src/backend/parser/parse_utilcmd.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "catalog/heap.h"
3333
#include "catalog/index.h"
3434
#include "catalog/namespace.h"
35+
#include "catalog/partition.h"
3536
#include "catalog/pg_am.h"
3637
#include "catalog/pg_collation.h"
3738
#include "catalog/pg_constraint.h"
@@ -3415,6 +3416,39 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
34153416
}
34163417

34173418

3419+
/*
3420+
* checkPartition
3421+
* Check that partRelOid is an oid of partition of the parent table rel
3422+
*/
3423+
static void
3424+
checkPartition(Relation rel, Oid partRelOid)
3425+
{
3426+
Relation partRel;
3427+
3428+
partRel = relation_open(partRelOid, AccessShareLock);
3429+
3430+
if (partRel->rd_rel->relkind != RELKIND_RELATION)
3431+
ereport(ERROR,
3432+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3433+
errmsg("\"%s\" is not a table",
3434+
RelationGetRelationName(partRel))));
3435+
3436+
if (!partRel->rd_rel->relispartition)
3437+
ereport(ERROR,
3438+
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3439+
errmsg("\"%s\" is not a partition",
3440+
RelationGetRelationName(partRel))));
3441+
3442+
if (get_partition_parent(partRelOid, false) != RelationGetRelid(rel))
3443+
ereport(ERROR,
3444+
(errcode(ERRCODE_UNDEFINED_TABLE),
3445+
errmsg("relation \"%s\" is not a partition of relation \"%s\"",
3446+
RelationGetRelationName(partRel),
3447+
RelationGetRelationName(rel))));
3448+
3449+
relation_close(partRel, AccessShareLock);
3450+
}
3451+
34183452
/*
34193453
* transformPartitionCmdForSplit
34203454
* Analyze the ALTER TABLLE ... SPLIT PARTITION command
@@ -3447,6 +3481,8 @@ transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
34473481

34483482
splitPartOid = RangeVarGetRelid(partcmd->name, NoLock, false);
34493483

3484+
checkPartition(parent, splitPartOid);
3485+
34503486
/* Then we should check partitions with transformed bounds. */
34513487
check_partitions_for_split(parent, splitPartOid, partcmd->name, partcmd->partlist, cxt->pstate);
34523488
}
@@ -3509,6 +3545,9 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
35093545
partOid = RangeVarGetRelid(name, NoLock, false);
35103546
if (partOid == defaultPartOid)
35113547
isDefaultPart = true;
3548+
3549+
checkPartition(parent, partOid);
3550+
35123551
partOids = lappend_oid(partOids, partOid);
35133552
}
35143553

src/test/regress/expected/partition_merge.out

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_fe
2525
ERROR: partition with name "sales_feb2022" already used
2626
LINE 1: ...e MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2...
2727
^
28-
-- ERROR: cannot merge non-table partition "sales_apr2022"
28+
-- ERROR: "sales_apr2022" is not a table
2929
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
30-
ERROR: cannot merge non-table partition "sales_apr2022"
30+
ERROR: "sales_apr2022" is not a table
3131
-- ERROR: invalid partitions order, partition "sales_mar2022" can not be merged
3232
-- (space between sections sales_jan2022 and sales_mar2022)
3333
ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_mar2022) INTO sales_jan_mar2022;
@@ -590,12 +590,12 @@ CREATE TABLE sales_nord2 PARTITION OF sales_list2 FOR VALUES IN ('Oslo', 'St. Pe
590590
CREATE TABLE sales_others2 PARTITION OF sales_list2 DEFAULT;
591591
CREATE TABLE sales_external (LIKE sales_list);
592592
CREATE TABLE sales_external2 (vch VARCHAR(5));
593-
-- ERROR: partition bound for relation "sales_external" is null
593+
-- ERROR: "sales_external" is not a partition
594594
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external) INTO sales_all;
595-
ERROR: partition bound for relation "sales_external" is null
596-
-- ERROR: partition bound for relation "sales_external2" is null
595+
ERROR: "sales_external" is not a partition
596+
-- ERROR: "sales_external2" is not a partition
597597
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external2) INTO sales_all;
598-
ERROR: partition bound for relation "sales_external2" is null
598+
ERROR: "sales_external2" is not a partition
599599
-- ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
600600
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_nord2, sales_east) INTO sales_all;
601601
ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
@@ -729,4 +729,21 @@ SELECT * FROM sales_list WHERE salesman_name = 'Ivanov';
729729
RESET enable_seqscan;
730730
DROP TABLE sales_list;
731731
--
732+
-- Try to MERGE partitions of another table.
733+
--
734+
CREATE TABLE t1 (i int, a int, b int, c int) PARTITION BY RANGE (a, b);
735+
CREATE TABLE t1p1 PARTITION OF t1 FOR VALUES FROM (1, 1) TO (1, 2);
736+
CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
737+
CREATE TABLE t2pa PARTITION OF t2 FOR VALUES FROM ('A') TO ('C');
738+
CREATE TABLE t3 (i int, t text);
739+
-- ERROR: relation "t1p1" is not a partition of relation "t2"
740+
ALTER TABLE t2 MERGE PARTITIONS (t1p1, t2pa) INTO t2p;
741+
ERROR: relation "t1p1" is not a partition of relation "t2"
742+
-- ERROR: "t3" is not a partition
743+
ALTER TABLE t2 MERGE PARTITIONS (t2pa, t3) INTO t2p;
744+
ERROR: "t3" is not a partition
745+
DROP TABLE t3;
746+
DROP TABLE t2;
747+
DROP TABLE t1;
748+
--
732749
DROP SCHEMA partitions_merge_schema;

src/test/regress/expected/partition_split.out

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,4 +1414,17 @@ SELECT * FROM sales_others;
14141414

14151415
DROP TABLE sales_range;
14161416
--
1417+
-- Try to SPLIT partition of another table.
1418+
--
1419+
CREATE TABLE t1(i int, t text) PARTITION BY LIST (t);
1420+
CREATE TABLE t1pa PARTITION OF t1 FOR VALUES IN ('A');
1421+
CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
1422+
-- ERROR: relation "t1pa" is not a partition of relation "t2"
1423+
ALTER TABLE t2 SPLIT PARTITION t1pa INTO
1424+
(PARTITION t2a FOR VALUES FROM ('A') TO ('B'),
1425+
PARTITION t2b FOR VALUES FROM ('B') TO ('C'));
1426+
ERROR: relation "t1pa" is not a partition of relation "t2"
1427+
DROP TABLE t2;
1428+
DROP TABLE t1;
1429+
--
14171430
DROP SCHEMA partition_split_schema;

src/test/regress/sql/partition_merge.sql

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
2828

2929
-- ERROR: partition with name "sales_feb2022" already used
3030
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2022) INTO sales_feb_mar_apr2022;
31-
-- ERROR: cannot merge non-table partition "sales_apr2022"
31+
-- ERROR: "sales_apr2022" is not a table
3232
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
3333
-- ERROR: invalid partitions order, partition "sales_mar2022" can not be merged
3434
-- (space between sections sales_jan2022 and sales_mar2022)
@@ -350,9 +350,9 @@ CREATE TABLE sales_others2 PARTITION OF sales_list2 DEFAULT;
350350
CREATE TABLE sales_external (LIKE sales_list);
351351
CREATE TABLE sales_external2 (vch VARCHAR(5));
352352

353-
-- ERROR: partition bound for relation "sales_external" is null
353+
-- ERROR: "sales_external" is not a partition
354354
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external) INTO sales_all;
355-
-- ERROR: partition bound for relation "sales_external2" is null
355+
-- ERROR: "sales_external2" is not a partition
356356
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external2) INTO sales_all;
357357
-- ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
358358
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_nord2, sales_east) INTO sales_all;
@@ -426,5 +426,23 @@ RESET enable_seqscan;
426426

427427
DROP TABLE sales_list;
428428

429+
--
430+
-- Try to MERGE partitions of another table.
431+
--
432+
CREATE TABLE t1 (i int, a int, b int, c int) PARTITION BY RANGE (a, b);
433+
CREATE TABLE t1p1 PARTITION OF t1 FOR VALUES FROM (1, 1) TO (1, 2);
434+
CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
435+
CREATE TABLE t2pa PARTITION OF t2 FOR VALUES FROM ('A') TO ('C');
436+
CREATE TABLE t3 (i int, t text);
437+
438+
-- ERROR: relation "t1p1" is not a partition of relation "t2"
439+
ALTER TABLE t2 MERGE PARTITIONS (t1p1, t2pa) INTO t2p;
440+
-- ERROR: "t3" is not a partition
441+
ALTER TABLE t2 MERGE PARTITIONS (t2pa, t3) INTO t2p;
442+
443+
DROP TABLE t3;
444+
DROP TABLE t2;
445+
DROP TABLE t1;
446+
429447
--
430448
DROP SCHEMA partitions_merge_schema;

src/test/regress/sql/partition_split.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,5 +829,20 @@ SELECT * FROM sales_others;
829829

830830
DROP TABLE sales_range;
831831

832+
--
833+
-- Try to SPLIT partition of another table.
834+
--
835+
CREATE TABLE t1(i int, t text) PARTITION BY LIST (t);
836+
CREATE TABLE t1pa PARTITION OF t1 FOR VALUES IN ('A');
837+
CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
838+
839+
-- ERROR: relation "t1pa" is not a partition of relation "t2"
840+
ALTER TABLE t2 SPLIT PARTITION t1pa INTO
841+
(PARTITION t2a FOR VALUES FROM ('A') TO ('B'),
842+
PARTITION t2b FOR VALUES FROM ('B') TO ('C'));
843+
844+
DROP TABLE t2;
845+
DROP TABLE t1;
846+
832847
--
833848
DROP SCHEMA partition_split_schema;

0 commit comments

Comments
 (0)