diff --git a/CHANGELOG.md b/CHANGELOG.md index 638ed2062..87bb3bff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/chunk_constraint.c b/src/chunk_constraint.c index 9e5365bf9..1802106f4 100644 --- a/src/chunk_constraint.c +++ b/src/chunk_constraint.c @@ -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) diff --git a/test/expected/create_hypertable.out b/test/expected/create_hypertable.out index 9c00a32c9..75a3fe14d 100644 --- a/test/expected/create_hypertable.out +++ b/test/expected/create_hypertable.out @@ -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; diff --git a/test/sql/create_hypertable.sql b/test/sql/create_hypertable.sql index 0ab7e5680..6321efe32 100644 --- a/test/sql/create_hypertable.sql +++ b/test/sql/create_hypertable.sql @@ -441,4 +441,162 @@ ALTER TABLE sample_table DROP COLUMN name; SELECT * FROM create_hypertable('sample_table', 'time'); -- cleanup -DROP TABLE sample_table CASCADE; \ No newline at end of file +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;