mirror of https://github.com/postgres/postgres
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
This commit is contained in:
parent
b1b13d2b52
commit
c99ef1811a
|
@ -21223,12 +21223,6 @@ ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
||||||
*/
|
*/
|
||||||
splitRel = table_openrv(cmd->name, AccessExclusiveLock);
|
splitRel = table_openrv(cmd->name, AccessExclusiveLock);
|
||||||
|
|
||||||
if (splitRel->rd_rel->relkind != RELKIND_RELATION)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
||||||
errmsg("cannot split non-table partition \"%s\"",
|
|
||||||
RelationGetRelationName(splitRel))));
|
|
||||||
|
|
||||||
splitRelOid = RelationGetRelid(splitRel);
|
splitRelOid = RelationGetRelid(splitRel);
|
||||||
|
|
||||||
/* Check descriptions of new partitions. */
|
/* Check descriptions of new partitions. */
|
||||||
|
@ -21463,12 +21457,6 @@ ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
||||||
*/
|
*/
|
||||||
mergingPartition = table_openrv(name, AccessExclusiveLock);
|
mergingPartition = table_openrv(name, AccessExclusiveLock);
|
||||||
|
|
||||||
if (mergingPartition->rd_rel->relkind != RELKIND_RELATION)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
||||||
errmsg("cannot merge non-table partition \"%s\"",
|
|
||||||
RelationGetRelationName(mergingPartition))));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checking that two partitions have the same name was before, in
|
* Checking that two partitions have the same name was before, in
|
||||||
* function transformPartitionCmdForMerge().
|
* function transformPartitionCmdForMerge().
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
|
#include "catalog/partition.h"
|
||||||
#include "catalog/pg_am.h"
|
#include "catalog/pg_am.h"
|
||||||
#include "catalog/pg_collation.h"
|
#include "catalog/pg_collation.h"
|
||||||
#include "catalog/pg_constraint.h"
|
#include "catalog/pg_constraint.h"
|
||||||
|
@ -3415,6 +3416,39 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* checkPartition
|
||||||
|
* Check that partRelOid is an oid of partition of the parent table rel
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
checkPartition(Relation rel, Oid partRelOid)
|
||||||
|
{
|
||||||
|
Relation partRel;
|
||||||
|
|
||||||
|
partRel = relation_open(partRelOid, AccessShareLock);
|
||||||
|
|
||||||
|
if (partRel->rd_rel->relkind != RELKIND_RELATION)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
|
errmsg("\"%s\" is not a table",
|
||||||
|
RelationGetRelationName(partRel))));
|
||||||
|
|
||||||
|
if (!partRel->rd_rel->relispartition)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||||
|
errmsg("\"%s\" is not a partition",
|
||||||
|
RelationGetRelationName(partRel))));
|
||||||
|
|
||||||
|
if (get_partition_parent(partRelOid, false) != RelationGetRelid(rel))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_TABLE),
|
||||||
|
errmsg("relation \"%s\" is not a partition of relation \"%s\"",
|
||||||
|
RelationGetRelationName(partRel),
|
||||||
|
RelationGetRelationName(rel))));
|
||||||
|
|
||||||
|
relation_close(partRel, AccessShareLock);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformPartitionCmdForSplit
|
* transformPartitionCmdForSplit
|
||||||
* Analyze the ALTER TABLLE ... SPLIT PARTITION command
|
* Analyze the ALTER TABLLE ... SPLIT PARTITION command
|
||||||
|
@ -3447,6 +3481,8 @@ transformPartitionCmdForSplit(CreateStmtContext *cxt, PartitionCmd *partcmd)
|
||||||
|
|
||||||
splitPartOid = RangeVarGetRelid(partcmd->name, NoLock, false);
|
splitPartOid = RangeVarGetRelid(partcmd->name, NoLock, false);
|
||||||
|
|
||||||
|
checkPartition(parent, splitPartOid);
|
||||||
|
|
||||||
/* Then we should check partitions with transformed bounds. */
|
/* Then we should check partitions with transformed bounds. */
|
||||||
check_partitions_for_split(parent, splitPartOid, partcmd->name, partcmd->partlist, cxt->pstate);
|
check_partitions_for_split(parent, splitPartOid, partcmd->name, partcmd->partlist, cxt->pstate);
|
||||||
}
|
}
|
||||||
|
@ -3509,6 +3545,9 @@ transformPartitionCmdForMerge(CreateStmtContext *cxt, PartitionCmd *partcmd)
|
||||||
partOid = RangeVarGetRelid(name, NoLock, false);
|
partOid = RangeVarGetRelid(name, NoLock, false);
|
||||||
if (partOid == defaultPartOid)
|
if (partOid == defaultPartOid)
|
||||||
isDefaultPart = true;
|
isDefaultPart = true;
|
||||||
|
|
||||||
|
checkPartition(parent, partOid);
|
||||||
|
|
||||||
partOids = lappend_oid(partOids, partOid);
|
partOids = lappend_oid(partOids, partOid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,9 @@ ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_fe
|
||||||
ERROR: partition with name "sales_feb2022" already used
|
ERROR: partition with name "sales_feb2022" already used
|
||||||
LINE 1: ...e MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2...
|
LINE 1: ...e MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2...
|
||||||
^
|
^
|
||||||
-- ERROR: cannot merge non-table partition "sales_apr2022"
|
-- ERROR: "sales_apr2022" is not a table
|
||||||
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
|
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
|
||||||
ERROR: cannot merge non-table partition "sales_apr2022"
|
ERROR: "sales_apr2022" is not a table
|
||||||
-- ERROR: invalid partitions order, partition "sales_mar2022" can not be merged
|
-- ERROR: invalid partitions order, partition "sales_mar2022" can not be merged
|
||||||
-- (space between sections sales_jan2022 and sales_mar2022)
|
-- (space between sections sales_jan2022 and sales_mar2022)
|
||||||
ALTER TABLE sales_range MERGE PARTITIONS (sales_jan2022, sales_mar2022) INTO sales_jan_mar2022;
|
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
|
||||||
CREATE TABLE sales_others2 PARTITION OF sales_list2 DEFAULT;
|
CREATE TABLE sales_others2 PARTITION OF sales_list2 DEFAULT;
|
||||||
CREATE TABLE sales_external (LIKE sales_list);
|
CREATE TABLE sales_external (LIKE sales_list);
|
||||||
CREATE TABLE sales_external2 (vch VARCHAR(5));
|
CREATE TABLE sales_external2 (vch VARCHAR(5));
|
||||||
-- ERROR: partition bound for relation "sales_external" is null
|
-- ERROR: "sales_external" is not a partition
|
||||||
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external) INTO sales_all;
|
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external) INTO sales_all;
|
||||||
ERROR: partition bound for relation "sales_external" is null
|
ERROR: "sales_external" is not a partition
|
||||||
-- ERROR: partition bound for relation "sales_external2" is null
|
-- ERROR: "sales_external2" is not a partition
|
||||||
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external2) INTO sales_all;
|
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external2) INTO sales_all;
|
||||||
ERROR: partition bound for relation "sales_external2" is null
|
ERROR: "sales_external2" is not a partition
|
||||||
-- ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
|
-- ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
|
||||||
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_nord2, sales_east) INTO sales_all;
|
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_nord2, sales_east) INTO sales_all;
|
||||||
ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
|
ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
|
||||||
|
@ -729,4 +729,21 @@ SELECT * FROM sales_list WHERE salesman_name = 'Ivanov';
|
||||||
RESET enable_seqscan;
|
RESET enable_seqscan;
|
||||||
DROP TABLE sales_list;
|
DROP TABLE sales_list;
|
||||||
--
|
--
|
||||||
|
-- Try to MERGE partitions of another table.
|
||||||
|
--
|
||||||
|
CREATE TABLE t1 (i int, a int, b int, c int) PARTITION BY RANGE (a, b);
|
||||||
|
CREATE TABLE t1p1 PARTITION OF t1 FOR VALUES FROM (1, 1) TO (1, 2);
|
||||||
|
CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
|
||||||
|
CREATE TABLE t2pa PARTITION OF t2 FOR VALUES FROM ('A') TO ('C');
|
||||||
|
CREATE TABLE t3 (i int, t text);
|
||||||
|
-- ERROR: relation "t1p1" is not a partition of relation "t2"
|
||||||
|
ALTER TABLE t2 MERGE PARTITIONS (t1p1, t2pa) INTO t2p;
|
||||||
|
ERROR: relation "t1p1" is not a partition of relation "t2"
|
||||||
|
-- ERROR: "t3" is not a partition
|
||||||
|
ALTER TABLE t2 MERGE PARTITIONS (t2pa, t3) INTO t2p;
|
||||||
|
ERROR: "t3" is not a partition
|
||||||
|
DROP TABLE t3;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
--
|
||||||
DROP SCHEMA partitions_merge_schema;
|
DROP SCHEMA partitions_merge_schema;
|
||||||
|
|
|
@ -1414,4 +1414,17 @@ SELECT * FROM sales_others;
|
||||||
|
|
||||||
DROP TABLE sales_range;
|
DROP TABLE sales_range;
|
||||||
--
|
--
|
||||||
|
-- Try to SPLIT partition of another table.
|
||||||
|
--
|
||||||
|
CREATE TABLE t1(i int, t text) PARTITION BY LIST (t);
|
||||||
|
CREATE TABLE t1pa PARTITION OF t1 FOR VALUES IN ('A');
|
||||||
|
CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
|
||||||
|
-- ERROR: relation "t1pa" is not a partition of relation "t2"
|
||||||
|
ALTER TABLE t2 SPLIT PARTITION t1pa INTO
|
||||||
|
(PARTITION t2a FOR VALUES FROM ('A') TO ('B'),
|
||||||
|
PARTITION t2b FOR VALUES FROM ('B') TO ('C'));
|
||||||
|
ERROR: relation "t1pa" is not a partition of relation "t2"
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
--
|
||||||
DROP SCHEMA partition_split_schema;
|
DROP SCHEMA partition_split_schema;
|
||||||
|
|
|
@ -28,7 +28,7 @@ CREATE TABLE sales_others PARTITION OF sales_range DEFAULT;
|
||||||
|
|
||||||
-- ERROR: partition with name "sales_feb2022" already used
|
-- ERROR: partition with name "sales_feb2022" already used
|
||||||
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2022) INTO sales_feb_mar_apr2022;
|
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_feb2022) INTO sales_feb_mar_apr2022;
|
||||||
-- ERROR: cannot merge non-table partition "sales_apr2022"
|
-- ERROR: "sales_apr2022" is not a table
|
||||||
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
|
ALTER TABLE sales_range MERGE PARTITIONS (sales_feb2022, sales_mar2022, sales_apr2022) INTO sales_feb_mar_apr2022;
|
||||||
-- ERROR: invalid partitions order, partition "sales_mar2022" can not be merged
|
-- ERROR: invalid partitions order, partition "sales_mar2022" can not be merged
|
||||||
-- (space between sections sales_jan2022 and sales_mar2022)
|
-- (space between sections sales_jan2022 and sales_mar2022)
|
||||||
|
@ -350,9 +350,9 @@ CREATE TABLE sales_others2 PARTITION OF sales_list2 DEFAULT;
|
||||||
CREATE TABLE sales_external (LIKE sales_list);
|
CREATE TABLE sales_external (LIKE sales_list);
|
||||||
CREATE TABLE sales_external2 (vch VARCHAR(5));
|
CREATE TABLE sales_external2 (vch VARCHAR(5));
|
||||||
|
|
||||||
-- ERROR: partition bound for relation "sales_external" is null
|
-- ERROR: "sales_external" is not a partition
|
||||||
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external) INTO sales_all;
|
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external) INTO sales_all;
|
||||||
-- ERROR: partition bound for relation "sales_external2" is null
|
-- ERROR: "sales_external2" is not a partition
|
||||||
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external2) INTO sales_all;
|
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_east, sales_external2) INTO sales_all;
|
||||||
-- ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
|
-- ERROR: relation "sales_nord2" is not a partition of relation "sales_list"
|
||||||
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_nord2, sales_east) INTO sales_all;
|
ALTER TABLE sales_list MERGE PARTITIONS (sales_west, sales_nord2, sales_east) INTO sales_all;
|
||||||
|
@ -426,5 +426,23 @@ RESET enable_seqscan;
|
||||||
|
|
||||||
DROP TABLE sales_list;
|
DROP TABLE sales_list;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Try to MERGE partitions of another table.
|
||||||
|
--
|
||||||
|
CREATE TABLE t1 (i int, a int, b int, c int) PARTITION BY RANGE (a, b);
|
||||||
|
CREATE TABLE t1p1 PARTITION OF t1 FOR VALUES FROM (1, 1) TO (1, 2);
|
||||||
|
CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
|
||||||
|
CREATE TABLE t2pa PARTITION OF t2 FOR VALUES FROM ('A') TO ('C');
|
||||||
|
CREATE TABLE t3 (i int, t text);
|
||||||
|
|
||||||
|
-- ERROR: relation "t1p1" is not a partition of relation "t2"
|
||||||
|
ALTER TABLE t2 MERGE PARTITIONS (t1p1, t2pa) INTO t2p;
|
||||||
|
-- ERROR: "t3" is not a partition
|
||||||
|
ALTER TABLE t2 MERGE PARTITIONS (t2pa, t3) INTO t2p;
|
||||||
|
|
||||||
|
DROP TABLE t3;
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
--
|
--
|
||||||
DROP SCHEMA partitions_merge_schema;
|
DROP SCHEMA partitions_merge_schema;
|
||||||
|
|
|
@ -829,5 +829,20 @@ SELECT * FROM sales_others;
|
||||||
|
|
||||||
DROP TABLE sales_range;
|
DROP TABLE sales_range;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Try to SPLIT partition of another table.
|
||||||
|
--
|
||||||
|
CREATE TABLE t1(i int, t text) PARTITION BY LIST (t);
|
||||||
|
CREATE TABLE t1pa PARTITION OF t1 FOR VALUES IN ('A');
|
||||||
|
CREATE TABLE t2 (i int, t text) PARTITION BY RANGE (t);
|
||||||
|
|
||||||
|
-- ERROR: relation "t1pa" is not a partition of relation "t2"
|
||||||
|
ALTER TABLE t2 SPLIT PARTITION t1pa INTO
|
||||||
|
(PARTITION t2a FOR VALUES FROM ('A') TO ('B'),
|
||||||
|
PARTITION t2b FOR VALUES FROM ('B') TO ('C'));
|
||||||
|
|
||||||
|
DROP TABLE t2;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
--
|
--
|
||||||
DROP SCHEMA partition_split_schema;
|
DROP SCHEMA partition_split_schema;
|
||||||
|
|
Loading…
Reference in New Issue