From ff347f8aff04865680c19ffc818460bb2afaad5b Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Thu, 4 Oct 2018 11:37:20 -0300 Subject: [PATCH] Fix duplicate primary keys in partitions When using the CREATE TABLE .. PARTITION OF syntax, it's possible to cause a partition to get two primary keys if the parent already has one. Tighten the check to disallow that. Reported-by: Rajkumar Raghuwanshi Author: Amul Sul Discussion: https://postgr.es/m/CAKcux6=OnSV3-qd8Gb6W=KPPwcCz6Fe_O_MQYjTa24__Xn8XxA@mail.gmail.com --- src/backend/catalog/index.c | 10 +++++----- src/test/regress/expected/indexing.out | 18 +++++++++++++++++- src/test/regress/sql/indexing.sql | 9 ++++++++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 4debe6ee87..3a24c32d14 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -218,12 +218,12 @@ index_check_primary_key(Relation heapRel, int i; /* - * If ALTER TABLE, check that there isn't already a PRIMARY KEY. In CREATE - * TABLE, we have faith that the parser rejected multiple pkey clauses; - * and CREATE INDEX doesn't have a way to say PRIMARY KEY, so it's no - * problem either. + * If ALTER TABLE and CREATE TABLE .. PARTITION OF, check that there isn't + * already a PRIMARY KEY. In CREATE TABLE for an ordinary relations, we + * have faith that the parser rejected multiple pkey clauses; and CREATE + * INDEX doesn't have a way to say PRIMARY KEY, so it's no problem either. */ - if (is_alter_table && + if ((is_alter_table || heapRel->rd_rel->relispartition) && relationHasPrimaryKey(heapRel)) { ereport(ERROR, diff --git a/src/test/regress/expected/indexing.out b/src/test/regress/expected/indexing.out index 8ab543ae31..225f4e9527 100644 --- a/src/test/regress/expected/indexing.out +++ b/src/test/regress/expected/indexing.out @@ -800,8 +800,24 @@ Indexes: "idxpart_pkey" PRIMARY KEY, btree (a) Number of partitions: 0 +-- multiple primary key on child should fail +create table failpart partition of idxpart (b primary key) for values from (0) to (100); +ERROR: multiple primary keys for table "failpart" are not allowed drop table idxpart; --- but not if you fail to use the full partition key +-- primary key on child is okay if there's no PK in the parent, though +create table idxpart (a int) partition by range (a); +create table idxpart1pk partition of idxpart (a primary key) for values from (0) to (100); +\d idxpart1pk + Table "public.idxpart1pk" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + a | integer | | not null | +Partition of: idxpart FOR VALUES FROM (0) TO (100) +Indexes: + "idxpart1pk_pkey" PRIMARY KEY, btree (a) + +drop table idxpart; +-- Failing to use the full partition key is not allowed create table idxpart (a int unique, b int) partition by range (a, b); ERROR: insufficient columns in UNIQUE constraint definition DETAIL: UNIQUE constraint on table "idxpart" lacks column "b" which is part of the partition key. diff --git a/src/test/regress/sql/indexing.sql b/src/test/regress/sql/indexing.sql index 48b8853219..f145384fbc 100644 --- a/src/test/regress/sql/indexing.sql +++ b/src/test/regress/sql/indexing.sql @@ -401,9 +401,16 @@ drop table idxpart; -- Verify that it works to add primary key / unique to partitioned tables create table idxpart (a int primary key, b int) partition by range (a); \d idxpart +-- multiple primary key on child should fail +create table failpart partition of idxpart (b primary key) for values from (0) to (100); +drop table idxpart; +-- primary key on child is okay if there's no PK in the parent, though +create table idxpart (a int) partition by range (a); +create table idxpart1pk partition of idxpart (a primary key) for values from (0) to (100); +\d idxpart1pk drop table idxpart; --- but not if you fail to use the full partition key +-- Failing to use the full partition key is not allowed create table idxpart (a int unique, b int) partition by range (a, b); create table idxpart (a int, b int unique) partition by range (a, b); create table idxpart (a int primary key, b int) partition by range (b, a);