Re: Issue attaching a table to a partitioned table with an auto-referenced foreign key - Mailing list pgsql-hackers
From | Guillaume Lelarge |
---|---|
Subject | Re: Issue attaching a table to a partitioned table with an auto-referenced foreign key |
Date | |
Msg-id | CAECtzeV9P5yGD6aq0rfDuJjPYAEEQuqmb4EN4p-1An3aAuVtyQ@mail.gmail.com Whole thread Raw |
In response to | Re: Issue attaching a table to a partitioned table with an auto-referenced foreign key (Guillaume Lelarge <guillaume@lelarge.info>) |
Responses |
Re: Issue attaching a table to a partitioned table with an auto-referenced foreign key
|
List | pgsql-hackers |
One last ping, hoping someone will have more time now than in january.
Perhaps my test is wrong, but I'd like to know why.
Thanks.
Le mar. 17 janv. 2023 à 16:53, Guillaume Lelarge <guillaume@lelarge.info> a écrit :
Quick ping, just to make sure someone can get a look at this issue :)Thanks.Le ven. 6 janv. 2023 à 11:07, Guillaume Lelarge <guillaume@lelarge.info> a écrit :Hello,
One of our customers has an issue with partitions and foreign keys. He works on a v13, but the issue is also present on v15.
I attach a SQL script showing the issue, and the results on 13.7, 13.9, and 15.1. But I'll explain the script here, and its behaviour on 13.9.
There is one partitioned table, two partitions and a foreign key. The foreign key references the same table:
create table t1 (
c1 bigint not null,
c1_old bigint null,
c2 bigint not null,
c2_old bigint null,
primary key (c1, c2)
)
partition by list (c1);
create table t1_a partition of t1 for values in (1);
create table t1_def partition of t1 default;
alter table t1 add foreign key (c1_old, c2_old) references t1 (c1, c2) on delete restrict on update restrict;
I've a SQL function that shows me some information from pg_constraints (code of the function in the SQL script attached). Here is the result of this function after creating the table, its partitions, and its foreign key:
select * from show_constraints();
conname | t | tref | coparent
------------------------+--------+--------+-----------------------
t1_c1_old_c2_old_fkey | t1 | t1 |
t1_c1_old_c2_old_fkey | t1_a | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey | t1_def | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey1 | t1 | t1_a | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey2 | t1 | t1_def | t1_c1_old_c2_old_fkey
(5 rows)
The constraint works great :
insert into t1 values(1, NULL, 2, NULL);
insert into t1 values(2, 1, 2, 2);
delete from t1 where c1 = 1;
psql:ticket15010_v3.sql:34: ERROR: update or delete on table "t1_a" violates foreign key constraint "t1_c1_old_c2_old_fkey1" on table "t1"
DETAIL: Key (c1, c2)=(1, 2) is still referenced from table "t1".
This error is normal since the line I want to delete is referenced on the other line.
If I try to detach the partition, it also gives me an error.
alter table t1 detach partition t1_a;
psql:ticket15010_v3.sql:36: ERROR: removing partition "t1_a" violates foreign key constraint "t1_c1_old_c2_old_fkey1"
DETAIL: Key (c1_old, c2_old)=(1, 2) is still referenced from table "t1".
Sounds good to me too (well, I'd like it to be smarter and find that the constraint is still good after the detach, but I can understand why it won't allow it).
The pg_constraint didn't change of course:
select * from show_constraints();
conname | t | tref | coparent
------------------------+--------+--------+-----------------------
t1_c1_old_c2_old_fkey | t1 | t1 |
t1_c1_old_c2_old_fkey | t1_a | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey | t1_def | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey1 | t1 | t1_a | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey2 | t1 | t1_def | t1_c1_old_c2_old_fkey
(5 rows)
Now, I'll delete the whole table contents, and I'll detach the partition:
delete from t1;
alter table t1 detach partition t1_a;
It seems to be working, but the content of pg_constraints is weird:
select * from show_constraints();
conname | t | tref | coparent
------------------------+--------+--------+-----------------------
t1_c1_old_c2_old_fkey | t1 | t1 |
t1_c1_old_c2_old_fkey | t1_a | t1 |
t1_c1_old_c2_old_fkey | t1_def | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey2 | t1 | t1_def | t1_c1_old_c2_old_fkey
(4 rows)
I understand why the ('t1_c1_old_c2_old_fkey1', 't1', 't1_a', 't1_c1_old_c2_old_fkey') tuple has gone but I don't understand why the ('t1_c1_old_c2_old_fkey', 't1_a', 't1', NULL) tuple is still there.
Anyway, I attach the partition:
alter table t1 attach partition t1_a for values in (1);
But pg_constraint has not changed:
select * from show_constraints();
conname | t | tref | coparent
------------------------+--------+--------+-----------------------
t1_c1_old_c2_old_fkey | t1 | t1 |
t1_c1_old_c2_old_fkey | t1_a | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey | t1_def | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey2 | t1 | t1_def | t1_c1_old_c2_old_fkey
(4 rows)
I was expecting to see the fifth tuple coming back, but alas, no.
And as a result, the foreign key doesn't work anymore:
insert into t1 values(1, NULL, 2, NULL);
insert into t1 values(2, 1, 2, 2);
delete from t1 where c1 = 1;
Well, let's truncate the partitioned table, and drop the partition:
truncate t1;
drop table t1_a;
The content of pg_constraint looks good to me:
select * from show_constraints();
conname | t | tref | coparent
------------------------+--------+--------+-----------------------
t1_c1_old_c2_old_fkey | t1 | t1 |
t1_c1_old_c2_old_fkey | t1_def | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey2 | t1 | t1_def | t1_c1_old_c2_old_fkey
(3 rows)
Let's create the partition to see if that works better:
create table t1_a partition of t1 for values in (1);
select * from show_constraints();
conname | t | tref | coparent
------------------------+--------+--------+-----------------------
t1_c1_old_c2_old_fkey | t1 | t1 |
t1_c1_old_c2_old_fkey | t1_a | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey | t1_def | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey2 | t1 | t1_def | t1_c1_old_c2_old_fkey
(4 rows)
insert into t1 values(1, NULL, 2, NULL);
INSERT 0 1
insert into t1 values(2, 1, 2, 2);
INSERT 0 1
delete from t1 where c1 = 1;
DELETE 1
Nope. I still miss the fifth tuple in pg_constraint, which results in a violated foreign key.
How about dropping the foreign key to create it once more:
truncate t1;
alter table t1 drop constraint t1_c1_old_c2_old_fkey;
select * from show_constraints();
conname | t | tref | coparent
---------+---+------+----------
(0 rows)
drop table t1_a;
create table t1_a partition of t1 for values in (1);
alter table t1 add foreign key (c1_old, c2_old) references t1 (c1, c2) on delete restrict on update restrict;
select * from show_constraints();
conname | t | tref | coparent
------------------------+--------+--------+-----------------------
t1_c1_old_c2_old_fkey | t1 | t1 |
t1_c1_old_c2_old_fkey | t1_a | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey | t1_def | t1 | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey1 | t1 | t1_a | t1_c1_old_c2_old_fkey
t1_c1_old_c2_old_fkey2 | t1 | t1_def | t1_c1_old_c2_old_fkey
(5 rows)
I have my fifth row back! And now, the foreign key works as it should:
insert into t1 values(1, NULL, 2, NULL);
insert into t1 values(2, 1, 2, 2);
delete from t1 where c1 = 1;
psql:ticket15010_v3.sql:87: ERROR: update or delete on table "t1_a" violates foreign key constraint "t1_c1_old_c2_old_fkey1" on table "t1"
DETAIL: Key (c1, c2)=(1, 2) is still referenced from table "t1".
This is what happens on 13.9 and 15.1. 13.7 shows another weird behaviour, but I guess I'll stop there. Everything is in the attached files.
I'd love to know if I did something wrong, if I didn't understand something, or if this is simply a bug.
Thanks.
Regards.
--Guillaume.
--Guillaume.
--
Guillaume.
pgsql-hackers by date: