diff --git a/.unreleased/pr_7301 b/.unreleased/pr_7301 new file mode 100644 index 00000000000..f8e502e7dfb --- /dev/null +++ b/.unreleased/pr_7301 @@ -0,0 +1 @@ +Fixes: #7301 Make foreign key behaviour for hypertables consistent diff --git a/src/hypertable.c b/src/hypertable.c index a0e6be8a5cb..73216d824d6 100644 --- a/src/hypertable.c +++ b/src/hypertable.c @@ -1319,7 +1319,10 @@ hypertable_validate_constraints(Oid relid) { Form_pg_constraint form = (Form_pg_constraint) GETSTRUCT(tuple); - if (form->contype == CONSTRAINT_FOREIGN) + /* + * Hypertable <-> hypertable foreign keys are not supported. + */ + if (form->contype == CONSTRAINT_FOREIGN && ts_hypertable_relid_to_id(form->conrelid) != -1) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("cannot have FOREIGN KEY constraints to hypertable \"%s\"", diff --git a/test/expected/create_hypertable.out b/test/expected/create_hypertable.out index 1e77e31ccb9..11c01e51d71 100644 --- a/test/expected/create_hypertable.out +++ b/test/expected/create_hypertable.out @@ -1054,7 +1054,6 @@ select * from tidrangescan_test where time > '2023-02-12 00:00:00+02:40'::timest (3 rows) drop table tidrangescan_test; -\set ON_ERROR_STOP 0 \set VERBOSITY default set client_min_messages = WARNING; -- test creating a hypertable from table referenced by a foreign key fails with @@ -1065,6 +1064,13 @@ create table test_schema.fk_child( id int, foreign key (time, id) references test_schema.fk_parent(time, id) ); +select create_hypertable ('test_schema.fk_child', 'time'); + create_hypertable +----------------------------- + (23,test_schema,fk_child,t) +(1 row) + +\set ON_ERROR_STOP 0 select create_hypertable ('test_schema.fk_parent', 'time'); ERROR: cannot have FOREIGN KEY constraints to hypertable "fk_parent" HINT: Remove all FOREIGN KEY constraints to table "fk_parent" before making it a hypertable. @@ -1088,8 +1094,8 @@ SELECT * FROM test.show_indexes('test') ORDER BY 1; SELECT * FROM show_chunks('test') ch, LATERAL test.show_indexes(ch) ORDER BY 1, 2; ch | Index | Columns | Expr | Unique | Primary | Exclusion | Tablespace ------------------------------------------+--------------------------------------------------------+---------+------+--------+---------+-----------+------------ - _timescaledb_internal._hyper_23_23_chunk | _timescaledb_internal._hyper_23_23_chunk_test_val_idx | {val} | | f | f | f | - _timescaledb_internal._hyper_23_23_chunk | _timescaledb_internal._hyper_23_23_chunk_test_time_idx | {time} | | f | f | f | + _timescaledb_internal._hyper_24_23_chunk | _timescaledb_internal._hyper_24_23_chunk_test_val_idx | {val} | | f | f | f | + _timescaledb_internal._hyper_24_23_chunk | _timescaledb_internal._hyper_24_23_chunk_test_time_idx | {time} | | f | f | f | (2 rows) DROP TABLE test; @@ -1112,6 +1118,6 @@ SELECT * FROM test.show_indexes('test') ORDER BY 1; SELECT * FROM show_chunks('test') ch, LATERAL test.show_indexes(ch) ORDER BY 1, 2; ch | Index | Columns | Expr | Unique | Primary | Exclusion | Tablespace ------------------------------------------+-------------------------------------------------------+---------+------+--------+---------+-----------+------------ - _timescaledb_internal._hyper_24_24_chunk | _timescaledb_internal._hyper_24_24_chunk_test_val_idx | {val} | | f | f | f | + _timescaledb_internal._hyper_25_24_chunk | _timescaledb_internal._hyper_25_24_chunk_test_val_idx | {val} | | f | f | f | (1 row) diff --git a/test/sql/create_hypertable.sql b/test/sql/create_hypertable.sql index af4fa200a07..62a1ca7adc3 100644 --- a/test/sql/create_hypertable.sql +++ b/test/sql/create_hypertable.sql @@ -632,7 +632,6 @@ select * from tidrangescan_test where time > '2023-02-12 00:00:00+02:40'::timest drop table tidrangescan_test; -\set ON_ERROR_STOP 0 \set VERBOSITY default set client_min_messages = WARNING; -- test creating a hypertable from table referenced by a foreign key fails with @@ -643,6 +642,8 @@ create table test_schema.fk_child( id int, foreign key (time, id) references test_schema.fk_parent(time, id) ); +select create_hypertable ('test_schema.fk_child', 'time'); +\set ON_ERROR_STOP 0 select create_hypertable ('test_schema.fk_parent', 'time'); \set ON_ERROR_STOP 1 diff --git a/tsl/test/expected/foreign_keys.out b/tsl/test/expected/foreign_keys.out index 6ce6572c121..df082f2d37b 100644 --- a/tsl/test/expected/foreign_keys.out +++ b/tsl/test/expected/foreign_keys.out @@ -1025,3 +1025,34 @@ SELECT create_hypertable('i7226', 'time'); CREATE TABLE i7226_valid(time timestamptz NOT NULL,device_id int NOT NULL, FOREIGN KEY(time, device_id) REFERENCES i7226(time, device_id)); INSERT INTO i7226 VALUES ('2024-08-29 12:00:00+00', 1); +-- test foreign key constraints that have been created before hypertable conversion +create table converted_pk(time timestamptz, id int, unique(time, id)); +create table converted_fk(time timestamptz, id int, foreign key (time, id) references converted_pk(time, id)); +select table_name FROM create_hypertable ('converted_pk', 'time'); +NOTICE: adding not-null constraint to column "time" + table_name +-------------- + converted_pk +(1 row) + +\set ON_ERROR_STOP 0 +-- should fail +INSERT INTO converted_fk SELECT '2020-01-01', 1; +ERROR: insert or update on table "converted_fk" violates foreign key constraint "converted_fk_time_id_fkey" +\set ON_ERROR_STOP 1 +INSERT INTO converted_pk SELECT '2020-01-01 0:01', 1; +\set ON_ERROR_STOP 0 +-- should still fail +INSERT INTO converted_fk SELECT '2020-01-01', 1; +ERROR: insert or update on table "converted_fk" violates foreign key constraint "converted_fk_time_id_fkey" +\set ON_ERROR_STOP 1 +INSERT INTO converted_fk SELECT '2020-01-01 0:01', 1; +\set ON_ERROR_STOP 0 +-- should fail +DELETE FROM converted_pk WHERE time = '2020-01-01 0:01'; +ERROR: update or delete on table "_hyper_16_27_chunk" violates foreign key constraint "converted_fk_time_id_fkey1" on table "converted_fk" +TRUNCATE converted_pk; +ERROR: cannot truncate a table referenced in a foreign key constraint +\set ON_ERROR_STOP 1 +DELETE FROM converted_fk; +DELETE FROM converted_pk WHERE time = '2020-01-01 0:01'; diff --git a/tsl/test/sql/foreign_keys.sql b/tsl/test/sql/foreign_keys.sql index f7b7ecd3368..8ff5d3d4df1 100644 --- a/tsl/test/sql/foreign_keys.sql +++ b/tsl/test/sql/foreign_keys.sql @@ -650,3 +650,31 @@ SELECT create_hypertable('i7226', 'time'); CREATE TABLE i7226_valid(time timestamptz NOT NULL,device_id int NOT NULL, FOREIGN KEY(time, device_id) REFERENCES i7226(time, device_id)); INSERT INTO i7226 VALUES ('2024-08-29 12:00:00+00', 1); +-- test foreign key constraints that have been created before hypertable conversion +create table converted_pk(time timestamptz, id int, unique(time, id)); +create table converted_fk(time timestamptz, id int, foreign key (time, id) references converted_pk(time, id)); + +select table_name FROM create_hypertable ('converted_pk', 'time'); + +\set ON_ERROR_STOP 0 +-- should fail +INSERT INTO converted_fk SELECT '2020-01-01', 1; +\set ON_ERROR_STOP 1 + +INSERT INTO converted_pk SELECT '2020-01-01 0:01', 1; + +\set ON_ERROR_STOP 0 +-- should still fail +INSERT INTO converted_fk SELECT '2020-01-01', 1; +\set ON_ERROR_STOP 1 +INSERT INTO converted_fk SELECT '2020-01-01 0:01', 1; + +\set ON_ERROR_STOP 0 +-- should fail +DELETE FROM converted_pk WHERE time = '2020-01-01 0:01'; +TRUNCATE converted_pk; +\set ON_ERROR_STOP 1 + +DELETE FROM converted_fk; +DELETE FROM converted_pk WHERE time = '2020-01-01 0:01'; +