Hypertable FK reference to partitioned table

Consider a hypertable which has a foreign key constraint on a
referenced table, which is a parititioned table. In such case, foreign
key constraint is duplicated for each of the partitioned table. When
we insert into a hypertable, we end up checking the foreign key
constraint multiple times, which obviously leads to foreign key
constraint violation. Instead we only check foreign key constraint of
the parent table of the partitioned table.

Fixes #4684
This commit is contained in:
Bharathy 2022-09-27 19:33:15 +05:30
parent 244b3e637c
commit f6dd55a191
4 changed files with 342 additions and 1 deletions

View File

@ -27,11 +27,13 @@ argument or resolve the type ambiguity by casting to the intended type.
* #4720 Fix chunk exclusion for prepared statements and dst changes
* #4738 Fix the assorted epoll_ctl() errors that could occur with COPY into a distributed hypertable
* #4739 Fix continuous aggregate migrate check constraint
* #4745 Fix FK constraint violation error while insert into hypertable which references partitioned table
**Thanks**
* @maxtwardowski for reporting problems with chunk exclusion and space dimensions
* @yuezhihan for reporting GROUP BY error when setting compress_segmentby with an enum column
* @carobme for reporting constraint error during continuous aggregate migration
* @jvanns for reporting hypertable FK reference to vanilla PostgreSQL partitioned table doesn't seem to work
## 2.8.0 (2022-08-30)

View File

@ -577,6 +577,15 @@ chunk_constraint_need_on_chunk(const char chunk_relkind, Form_pg_constraint conf
*/
return false;
}
/*
Check if the foreign key constraint references a partition in a partitioned
table. In that case, we shouldn't include this constraint as we will end up
checking the foreign key constraint once for every partition, which obviously
leads to foreign key constraint violation. Instead, we only include constraints
referencing the parent table of the partitioned table.
*/
if (conform->contype == CONSTRAINT_FOREIGN && OidIsValid(conform->conparentid))
return false;
/* Foreign tables do not support non-check constraints, so skip them */
if (chunk_relkind == RELKIND_FOREIGN_TABLE)

View File

@ -802,3 +802,175 @@ SELECT * FROM create_hypertable('sample_table', 'time');
-- cleanup
DROP TABLE sample_table CASCADE;
-- github issue 4684
-- test PARTITION BY HASH
CREATE TABLE regular(
id INT NOT NULL,
dev INT NOT NULL,
value INT,
CONSTRAINT cstr_regular_pky PRIMARY KEY (id)
) PARTITION BY HASH (id);
DO $$
BEGIN
FOR i IN 1..2
LOOP
EXECUTE format('
CREATE TABLE %I
PARTITION OF regular
FOR VALUES WITH (MODULUS 2, REMAINDER %s)',
'regular_' || i, i - 1
);
END LOOP;
END;
$$;
INSERT INTO regular SELECT generate_series(1,1000), 44,55;
CREATE TABLE timescale (
ts TIMESTAMP WITH TIME ZONE NOT NULL,
id INT NOT NULL,
dev INT NOT NULL,
FOREIGN KEY (id) REFERENCES regular(id) ON DELETE CASCADE
);
SELECT create_hypertable(
relation => 'timescale',
time_column_name => 'ts'
);
create_hypertable
-------------------------
(19,public,timescale,t)
(1 row)
-- creates chunk1
INSERT INTO timescale SELECT now(), generate_series(1,200), 43;
-- creates chunk2
INSERT INTO timescale SELECT now() + interval '20' day, generate_series(1,200), 43;
-- creates chunk3
INSERT INTO timescale SELECT now() + interval '40' day, generate_series(1,200), 43;
-- show chunks
SELECT SHOW_CHUNKS('timescale');
show_chunks
------------------------------------------
_timescaledb_internal._hyper_19_15_chunk
_timescaledb_internal._hyper_19_16_chunk
_timescaledb_internal._hyper_19_17_chunk
(3 rows)
\set ON_ERROR_STOP 0
-- record goes into chunk1 violating FK constraint as value 1001 is not present in regular table
INSERT INTO timescale SELECT now(), 1001, 43;
ERROR: insert or update on table "_hyper_19_15_chunk" violates foreign key constraint "15_1_timescale_id_fkey"
-- record goes into chunk2 violating FK constraint as value 1002 is not present in regular table
INSERT INTO timescale SELECT now() + interval '20' day, 1002, 43;
ERROR: insert or update on table "_hyper_19_16_chunk" violates foreign key constraint "16_2_timescale_id_fkey"
-- record goes into chunk3 violating FK constraint as value 1003 is not present in regular table
INSERT INTO timescale SELECT now() + interval '40' day, 1003, 43;
ERROR: insert or update on table "_hyper_19_17_chunk" violates foreign key constraint "17_3_timescale_id_fkey"
\set ON_ERROR_STOP 1
-- cleanup
DROP TABLE regular cascade;
NOTICE: drop cascades to 4 other objects
DROP TABLE timescale cascade;
-- test PARTITION BY RANGE
CREATE TABLE regular(
id INT NOT NULL,
dev INT NOT NULL,
value INT,
CONSTRAINT cstr_regular_pky PRIMARY KEY (id)
) PARTITION BY RANGE (id);
CREATE TABLE regular_1_500 PARTITION OF regular
FOR VALUES FROM (1) TO (500);
CREATE TABLE regular_500_1000 PARTITION OF regular
FOR VALUES FROM (500) TO (801);
INSERT INTO regular SELECT generate_series(1,800), 44,55;
CREATE TABLE timescale (
ts TIMESTAMP WITH TIME ZONE NOT NULL,
id INT NOT NULL,
dev INT NOT NULL,
FOREIGN KEY (id) REFERENCES regular(id) ON DELETE CASCADE
);
SELECT create_hypertable(
relation => 'timescale',
time_column_name => 'ts'
);
create_hypertable
-------------------------
(20,public,timescale,t)
(1 row)
-- creates chunk1
INSERT INTO timescale SELECT now(), generate_series(1,200), 43;
-- creates chunk2
INSERT INTO timescale SELECT now() + interval '20' day, generate_series(200,400), 43;
-- creates chunk3
INSERT INTO timescale SELECT now() + interval '40' day, generate_series(400,600), 43;
-- show chunks
SELECT SHOW_CHUNKS('timescale');
show_chunks
------------------------------------------
_timescaledb_internal._hyper_20_18_chunk
_timescaledb_internal._hyper_20_19_chunk
_timescaledb_internal._hyper_20_20_chunk
(3 rows)
\set ON_ERROR_STOP 0
-- FK constraint violation as value 801 is not present in regular table
INSERT INTO timescale SELECT now(), 801, 43;
ERROR: insert or update on table "_hyper_20_18_chunk" violates foreign key constraint "18_4_timescale_id_fkey"
-- FK constraint violation as value 902 is not present in regular table
INSERT INTO timescale SELECT now() + interval '20' day, 902, 43;
ERROR: insert or update on table "_hyper_20_19_chunk" violates foreign key constraint "19_5_timescale_id_fkey"
-- FK constraint violation as value 1003 is not present in regular table
INSERT INTO timescale SELECT now() + interval '40' day, 1003, 43;
ERROR: insert or update on table "_hyper_20_20_chunk" violates foreign key constraint "20_6_timescale_id_fkey"
\set ON_ERROR_STOP 1
-- cleanup
DROP TABLE regular cascade;
NOTICE: drop cascades to 4 other objects
DROP TABLE timescale cascade;
-- test PARTITION BY LIST
CREATE TABLE regular(
id INT NOT NULL,
dev INT NOT NULL,
value INT,
CONSTRAINT cstr_regular_pky PRIMARY KEY (id)
) PARTITION BY LIST (id);
CREATE TABLE regular_1_2_3_4 PARTITION OF regular FOR VALUES IN (1,2,3,4);
CREATE TABLE regular_5_6_7_8 PARTITION OF regular FOR VALUES IN (5,6,7,8);
INSERT INTO regular SELECT generate_series(1,8), 44,55;
CREATE TABLE timescale (
ts TIMESTAMP WITH TIME ZONE NOT NULL,
id INT NOT NULL,
dev INT NOT NULL,
FOREIGN KEY (id) REFERENCES regular(id) ON DELETE CASCADE
);
SELECT create_hypertable(
relation => 'timescale',
time_column_name => 'ts'
);
create_hypertable
-------------------------
(21,public,timescale,t)
(1 row)
insert into timescale values (now(), 1,2);
insert into timescale values (now(), 2,2);
insert into timescale values (now(), 3,2);
insert into timescale values (now(), 4,2);
insert into timescale values (now(), 5,2);
insert into timescale values (now(), 6,2);
insert into timescale values (now(), 7,2);
insert into timescale values (now(), 8,2);
\set ON_ERROR_STOP 0
-- FK constraint violation as value 9 is not present in regular table
insert into timescale values (now(), 9,2);
ERROR: insert or update on table "_hyper_21_21_chunk" violates foreign key constraint "21_7_timescale_id_fkey"
-- FK constraint violation as value 10 is not present in regular table
insert into timescale values (now(), 10,2);
ERROR: insert or update on table "_hyper_21_21_chunk" violates foreign key constraint "21_7_timescale_id_fkey"
-- FK constraint violation as value 111 is not present in regular table
insert into timescale values (now(), 111,2);
ERROR: insert or update on table "_hyper_21_21_chunk" violates foreign key constraint "21_7_timescale_id_fkey"
\set ON_ERROR_STOP 1
-- cleanup
DROP TABLE regular cascade;
NOTICE: drop cascades to 2 other objects
DROP TABLE timescale cascade;

View File

@ -441,4 +441,162 @@ ALTER TABLE sample_table DROP COLUMN name;
SELECT * FROM create_hypertable('sample_table', 'time');
-- cleanup
DROP TABLE sample_table CASCADE;
DROP TABLE sample_table CASCADE;
-- github issue 4684
-- test PARTITION BY HASH
CREATE TABLE regular(
id INT NOT NULL,
dev INT NOT NULL,
value INT,
CONSTRAINT cstr_regular_pky PRIMARY KEY (id)
) PARTITION BY HASH (id);
DO $$
BEGIN
FOR i IN 1..2
LOOP
EXECUTE format('
CREATE TABLE %I
PARTITION OF regular
FOR VALUES WITH (MODULUS 2, REMAINDER %s)',
'regular_' || i, i - 1
);
END LOOP;
END;
$$;
INSERT INTO regular SELECT generate_series(1,1000), 44,55;
CREATE TABLE timescale (
ts TIMESTAMP WITH TIME ZONE NOT NULL,
id INT NOT NULL,
dev INT NOT NULL,
FOREIGN KEY (id) REFERENCES regular(id) ON DELETE CASCADE
);
SELECT create_hypertable(
relation => 'timescale',
time_column_name => 'ts'
);
-- creates chunk1
INSERT INTO timescale SELECT now(), generate_series(1,200), 43;
-- creates chunk2
INSERT INTO timescale SELECT now() + interval '20' day, generate_series(1,200), 43;
-- creates chunk3
INSERT INTO timescale SELECT now() + interval '40' day, generate_series(1,200), 43;
-- show chunks
SELECT SHOW_CHUNKS('timescale');
\set ON_ERROR_STOP 0
-- record goes into chunk1 violating FK constraint as value 1001 is not present in regular table
INSERT INTO timescale SELECT now(), 1001, 43;
-- record goes into chunk2 violating FK constraint as value 1002 is not present in regular table
INSERT INTO timescale SELECT now() + interval '20' day, 1002, 43;
-- record goes into chunk3 violating FK constraint as value 1003 is not present in regular table
INSERT INTO timescale SELECT now() + interval '40' day, 1003, 43;
\set ON_ERROR_STOP 1
-- cleanup
DROP TABLE regular cascade;
DROP TABLE timescale cascade;
-- test PARTITION BY RANGE
CREATE TABLE regular(
id INT NOT NULL,
dev INT NOT NULL,
value INT,
CONSTRAINT cstr_regular_pky PRIMARY KEY (id)
) PARTITION BY RANGE (id);
CREATE TABLE regular_1_500 PARTITION OF regular
FOR VALUES FROM (1) TO (500);
CREATE TABLE regular_500_1000 PARTITION OF regular
FOR VALUES FROM (500) TO (801);
INSERT INTO regular SELECT generate_series(1,800), 44,55;
CREATE TABLE timescale (
ts TIMESTAMP WITH TIME ZONE NOT NULL,
id INT NOT NULL,
dev INT NOT NULL,
FOREIGN KEY (id) REFERENCES regular(id) ON DELETE CASCADE
);
SELECT create_hypertable(
relation => 'timescale',
time_column_name => 'ts'
);
-- creates chunk1
INSERT INTO timescale SELECT now(), generate_series(1,200), 43;
-- creates chunk2
INSERT INTO timescale SELECT now() + interval '20' day, generate_series(200,400), 43;
-- creates chunk3
INSERT INTO timescale SELECT now() + interval '40' day, generate_series(400,600), 43;
-- show chunks
SELECT SHOW_CHUNKS('timescale');
\set ON_ERROR_STOP 0
-- FK constraint violation as value 801 is not present in regular table
INSERT INTO timescale SELECT now(), 801, 43;
-- FK constraint violation as value 902 is not present in regular table
INSERT INTO timescale SELECT now() + interval '20' day, 902, 43;
-- FK constraint violation as value 1003 is not present in regular table
INSERT INTO timescale SELECT now() + interval '40' day, 1003, 43;
\set ON_ERROR_STOP 1
-- cleanup
DROP TABLE regular cascade;
DROP TABLE timescale cascade;
-- test PARTITION BY LIST
CREATE TABLE regular(
id INT NOT NULL,
dev INT NOT NULL,
value INT,
CONSTRAINT cstr_regular_pky PRIMARY KEY (id)
) PARTITION BY LIST (id);
CREATE TABLE regular_1_2_3_4 PARTITION OF regular FOR VALUES IN (1,2,3,4);
CREATE TABLE regular_5_6_7_8 PARTITION OF regular FOR VALUES IN (5,6,7,8);
INSERT INTO regular SELECT generate_series(1,8), 44,55;
CREATE TABLE timescale (
ts TIMESTAMP WITH TIME ZONE NOT NULL,
id INT NOT NULL,
dev INT NOT NULL,
FOREIGN KEY (id) REFERENCES regular(id) ON DELETE CASCADE
);
SELECT create_hypertable(
relation => 'timescale',
time_column_name => 'ts'
);
insert into timescale values (now(), 1,2);
insert into timescale values (now(), 2,2);
insert into timescale values (now(), 3,2);
insert into timescale values (now(), 4,2);
insert into timescale values (now(), 5,2);
insert into timescale values (now(), 6,2);
insert into timescale values (now(), 7,2);
insert into timescale values (now(), 8,2);
\set ON_ERROR_STOP 0
-- FK constraint violation as value 9 is not present in regular table
insert into timescale values (now(), 9,2);
-- FK constraint violation as value 10 is not present in regular table
insert into timescale values (now(), 10,2);
-- FK constraint violation as value 111 is not present in regular table
insert into timescale values (now(), 111,2);
\set ON_ERROR_STOP 1
-- cleanup
DROP TABLE regular cascade;
DROP TABLE timescale cascade;