From 6d83e0584d6920473ea309f228a0690e2bb092a0 Mon Sep 17 00:00:00 2001
From: Sven Klemm <sven@timescale.com>
Date: Thu, 18 Jul 2024 10:21:57 +0200
Subject: [PATCH] Add more tests for foreign key constraints on hypertables

---
 tsl/test/expected/foreign_keys.out | 322 +++++++++++++++++++++++++++++
 tsl/test/sql/foreign_keys.sql      | 209 +++++++++++++++++++
 2 files changed, 531 insertions(+)

diff --git a/tsl/test/expected/foreign_keys.out b/tsl/test/expected/foreign_keys.out
index 416d287a94e..5fcf371f831 100644
--- a/tsl/test/expected/foreign_keys.out
+++ b/tsl/test/expected/foreign_keys.out
@@ -580,3 +580,325 @@ ERROR:  hypertables cannot be used as foreign key references of hypertables
 DROP TABLE event;
 DROP TABLE event2;
 DROP TABLE metrics;
+-- test FK behavior with different cascading configurations
+CREATE TABLE fk_no_action(fk_no_action text primary key);
+CREATE TABLE fk_restrict(fk_restrict text primary key);
+CREATE TABLE fk_cascade(fk_cascade text primary key);
+CREATE TABLE fk_set_null(fk_set_null text primary key);
+CREATE TABLE fk_set_default(fk_set_default text primary key);
+CREATE TABLE ht(
+  time timestamptz not null,
+  fk_no_action text references fk_no_action(fk_no_action) ON DELETE NO ACTION ON UPDATE NO ACTION,
+  fk_restrict text references fk_restrict(fk_restrict) ON DELETE RESTRICT ON UPDATE RESTRICT,
+  fk_cascade text references fk_cascade(fk_cascade) ON DELETE CASCADE ON UPDATE CASCADE,
+  fk_set_null text references fk_set_null(fk_set_null) ON DELETE SET NULL ON UPDATE SET NULL,
+  fk_set_default text default 'default' references fk_set_default(fk_set_default) ON DELETE SET DEFAULT ON UPDATE SET DEFAULT
+);
+SELECT table_name FROM create_hypertable('ht', 'time');
+ table_name 
+------------
+ ht
+(1 row)
+
+ALTER TABLE ht SET(timescaledb.compress, timescaledb.compress_segmentby='fk_no_action,fk_restrict,fk_cascade,fk_set_null,fk_set_default');
+NOTICE:  default order by for hypertable "ht" is set to ""time" DESC"
+\set ON_ERROR_STOP 0
+INSERT INTO fk_set_default(fk_set_default) VALUES ('default');
+INSERT INTO ht(time) VALUES ('2020-01-01');
+-- NO ACTION
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+ERROR:  insert or update on table "_hyper_13_14_chunk" violates foreign key constraint "14_14_ht_fk_no_action_fkey"
+-- ON UPDATE NO ACTION
+BEGIN;
+INSERT INTO fk_no_action(fk_no_action) VALUES ('fk_no_action');
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+-- should error
+UPDATE fk_no_action SET fk_no_action = 'fk_no_action_updated';
+ERROR:  update or delete on table "fk_no_action" violates foreign key constraint "ht_fk_no_action_fkey" on table "ht"
+ROLLBACK;
+-- ON UPDATE NO ACTION with compression
+BEGIN;
+INSERT INTO fk_no_action(fk_no_action) VALUES ('fk_no_action');
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+ count 
+-------
+     1
+(1 row)
+
+-- should error
+UPDATE fk_no_action SET fk_no_action = 'fk_no_action_updated';
+ERROR:  update or delete on table "fk_no_action" violates foreign key constraint "ht_fk_no_action_fkey" on table "ht"
+ROLLBACK;
+-- ON DELETE NO ACTION
+BEGIN;
+INSERT INTO fk_no_action(fk_no_action) VALUES ('fk_no_action');
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+-- should error
+DELETE FROM fk_no_action;
+ERROR:  update or delete on table "fk_no_action" violates foreign key constraint "ht_fk_no_action_fkey" on table "ht"
+ROLLBACK;
+-- ON DELETE NO ACTION with compression
+BEGIN;
+INSERT INTO fk_no_action(fk_no_action) VALUES ('fk_no_action');
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+ count 
+-------
+     1
+(1 row)
+
+-- should error
+DELETE FROM fk_no_action;
+ERROR:  update or delete on table "fk_no_action" violates foreign key constraint "ht_fk_no_action_fkey" on table "ht"
+ROLLBACK;
+-- RESTRICT
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+ERROR:  insert or update on table "_hyper_13_14_chunk" violates foreign key constraint "14_15_ht_fk_restrict_fkey"
+-- ON UPDATE RESTRICT
+BEGIN;
+INSERT INTO fk_restrict(fk_restrict) VALUES ('fk_restrict');
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+-- should error
+UPDATE fk_restrict SET fk_restrict = 'fk_restrict_updated';
+ERROR:  update or delete on table "fk_restrict" violates foreign key constraint "ht_fk_restrict_fkey" on table "ht"
+ROLLBACK;
+-- ON UPDATE RESTRICT with compression
+BEGIN;
+INSERT INTO fk_restrict(fk_restrict) VALUES ('fk_restrict');
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+ count 
+-------
+     1
+(1 row)
+
+-- should error
+UPDATE fk_restrict SET fk_restrict = 'fk_restrict_updated';
+ERROR:  update or delete on table "fk_restrict" violates foreign key constraint "ht_fk_restrict_fkey" on table "ht"
+ROLLBACK;
+-- ON DELETE RESTRICT
+BEGIN;
+INSERT INTO fk_restrict(fk_restrict) VALUES ('fk_restrict');
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+-- should error
+DELETE FROM fk_restrict;
+ERROR:  update or delete on table "fk_restrict" violates foreign key constraint "ht_fk_restrict_fkey" on table "ht"
+ROLLBACK;
+-- ON DELETE RESTRICT with compression
+BEGIN;
+INSERT INTO fk_restrict(fk_restrict) VALUES ('fk_restrict');
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+ count 
+-------
+     1
+(1 row)
+
+-- should error
+DELETE FROM fk_restrict;
+ERROR:  update or delete on table "fk_restrict" violates foreign key constraint "ht_fk_restrict_fkey" on table "ht"
+ROLLBACK;
+-- CASCADE
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+ERROR:  insert or update on table "_hyper_13_14_chunk" violates foreign key constraint "14_13_ht_fk_cascade_fkey"
+-- ON UPDATE CASCADE
+BEGIN;
+INSERT INTO fk_cascade(fk_cascade) VALUES ('fk_cascade');
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+-- should cascade
+UPDATE fk_cascade SET fk_cascade = 'fk_cascade_updated';
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict |     fk_cascade     | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+--------------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |                    |             | default
+ Wed Jan 01 00:00:00 2020 PST |              |             | fk_cascade_updated |             | default
+(2 rows)
+
+ROLLBACK;
+-- ON UPDATE CASCADE with compression
+BEGIN;
+INSERT INTO fk_cascade(fk_cascade) VALUES ('fk_cascade');
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+ count 
+-------
+     1
+(1 row)
+
+-- should cascade
+UPDATE fk_cascade SET fk_cascade = 'fk_cascade_updated';
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict |     fk_cascade     | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+--------------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |                    |             | default
+ Wed Jan 01 00:00:00 2020 PST |              |             | fk_cascade_updated |             | default
+(2 rows)
+
+EXPLAIN (analyze, costs off, timing off, summary off) SELECT * FROM ht;
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
+ Custom Scan (DecompressChunk) on _hyper_13_14_chunk (actual rows=2 loops=1)
+   ->  Seq Scan on compress_hyper_14_19_chunk (actual rows=2 loops=1)
+(2 rows)
+
+ROLLBACK;
+-- ON DELETE CASCADE
+BEGIN;
+INSERT INTO fk_cascade(fk_cascade) VALUES ('fk_cascade');
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+-- should cascade
+DELETE FROM fk_cascade;
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict | fk_cascade | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+(1 row)
+
+ROLLBACK;
+-- ON DELETE CASCADE with compression
+BEGIN;
+INSERT INTO fk_cascade(fk_cascade) VALUES ('fk_cascade');
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+ count 
+-------
+     1
+(1 row)
+
+-- should cascade
+DELETE FROM fk_cascade;
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict | fk_cascade | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+(1 row)
+
+EXPLAIN (analyze, costs off, timing off, summary off) SELECT * FROM ht;
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
+ Custom Scan (DecompressChunk) on _hyper_13_14_chunk (actual rows=1 loops=1)
+   ->  Seq Scan on compress_hyper_14_20_chunk (actual rows=1 loops=1)
+(2 rows)
+
+ROLLBACK;
+-- SET NULL
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+ERROR:  insert or update on table "_hyper_13_14_chunk" violates foreign key constraint "14_17_ht_fk_set_null_fkey"
+-- ON UPDATE SET NULL
+BEGIN;
+INSERT INTO fk_set_null(fk_set_null) VALUES ('fk_set_null');
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+-- should set column to null
+UPDATE fk_set_null SET fk_set_null = 'fk_set_null_updated';
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict | fk_cascade | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+(2 rows)
+
+ROLLBACK;
+-- ON UPDATE SET NULL with compression
+BEGIN;
+INSERT INTO fk_set_null(fk_set_null) VALUES ('fk_set_null');
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+ count 
+-------
+     1
+(1 row)
+
+-- should set column to null
+UPDATE fk_set_null SET fk_set_null = 'fk_set_null_updated';
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict | fk_cascade | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+(2 rows)
+
+EXPLAIN (analyze, costs off, timing off, summary off) SELECT * FROM ht;
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
+ Custom Scan (DecompressChunk) on _hyper_13_14_chunk (actual rows=2 loops=1)
+   ->  Seq Scan on compress_hyper_14_21_chunk (actual rows=2 loops=1)
+(2 rows)
+
+ROLLBACK;
+-- ON DELETE SET NULL
+BEGIN;
+INSERT INTO fk_set_null(fk_set_null) VALUES ('fk_set_null');
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+-- should set column to null
+DELETE FROM fk_set_null;
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict | fk_cascade | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+(2 rows)
+
+ROLLBACK;
+-- ON DELETE SET NULL with compression
+BEGIN;
+INSERT INTO fk_set_null(fk_set_null) VALUES ('fk_set_null');
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+ count 
+-------
+     1
+(1 row)
+
+-- should set column to null
+DELETE FROM fk_set_null;
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict | fk_cascade | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+(2 rows)
+
+EXPLAIN (analyze, costs off, timing off, summary off) SELECT * FROM ht;
+                                 QUERY PLAN                                  
+-----------------------------------------------------------------------------
+ Custom Scan (DecompressChunk) on _hyper_13_14_chunk (actual rows=2 loops=1)
+   ->  Seq Scan on compress_hyper_14_22_chunk (actual rows=2 loops=1)
+(2 rows)
+
+ROLLBACK;
+-- SET DEFAULT
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_set_default) VALUES ('2020-01-01', 'fk_set_default');
+ERROR:  insert or update on table "_hyper_13_14_chunk" violates foreign key constraint "14_16_ht_fk_set_default_fkey"
+-- set default is not supported for hypertables so these will fail
+BEGIN;
+INSERT INTO fk_set_default(fk_set_default) VALUES ('fk_set_default');
+INSERT INTO ht(time, fk_set_default) VALUES ('2020-01-01', 'fk_set_default');
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict | fk_cascade | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | fk_set_default
+(2 rows)
+
+UPDATE fk_set_default SET fk_set_default = 'fk_set_default_updated' WHERE fk_set_default = 'fk_set_default';
+ERROR:  update or delete on table "fk_set_default" violates foreign key constraint "ht_fk_set_default_fkey" on table "ht"
+ROLLBACK;
+BEGIN;
+INSERT INTO fk_set_default(fk_set_default) VALUES ('fk_set_default');
+INSERT INTO ht(time, fk_set_default) VALUES ('2020-01-01', 'fk_set_default');
+SELECT * FROM ht;
+             time             | fk_no_action | fk_restrict | fk_cascade | fk_set_null | fk_set_default 
+------------------------------+--------------+-------------+------------+-------------+----------------
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | default
+ Wed Jan 01 00:00:00 2020 PST |              |             |            |             | fk_set_default
+(2 rows)
+
+DELETE FROM fk_set_default WHERE fk_set_default = 'fk_set_default';
+ERROR:  update or delete on table "fk_set_default" violates foreign key constraint "ht_fk_set_default_fkey" on table "ht"
+ROLLBACK;
diff --git a/tsl/test/sql/foreign_keys.sql b/tsl/test/sql/foreign_keys.sql
index ba760972e8d..93ec2b04720 100644
--- a/tsl/test/sql/foreign_keys.sql
+++ b/tsl/test/sql/foreign_keys.sql
@@ -395,3 +395,212 @@ SELECT table_name FROM create_hypertable('event2', 'time');
 DROP TABLE event;
 DROP TABLE event2;
 DROP TABLE metrics;
+
+-- test FK behavior with different cascading configurations
+CREATE TABLE fk_no_action(fk_no_action text primary key);
+CREATE TABLE fk_restrict(fk_restrict text primary key);
+CREATE TABLE fk_cascade(fk_cascade text primary key);
+CREATE TABLE fk_set_null(fk_set_null text primary key);
+CREATE TABLE fk_set_default(fk_set_default text primary key);
+
+CREATE TABLE ht(
+  time timestamptz not null,
+  fk_no_action text references fk_no_action(fk_no_action) ON DELETE NO ACTION ON UPDATE NO ACTION,
+  fk_restrict text references fk_restrict(fk_restrict) ON DELETE RESTRICT ON UPDATE RESTRICT,
+  fk_cascade text references fk_cascade(fk_cascade) ON DELETE CASCADE ON UPDATE CASCADE,
+  fk_set_null text references fk_set_null(fk_set_null) ON DELETE SET NULL ON UPDATE SET NULL,
+  fk_set_default text default 'default' references fk_set_default(fk_set_default) ON DELETE SET DEFAULT ON UPDATE SET DEFAULT
+);
+
+SELECT table_name FROM create_hypertable('ht', 'time');
+
+ALTER TABLE ht SET(timescaledb.compress, timescaledb.compress_segmentby='fk_no_action,fk_restrict,fk_cascade,fk_set_null,fk_set_default');
+
+\set ON_ERROR_STOP 0
+
+INSERT INTO fk_set_default(fk_set_default) VALUES ('default');
+INSERT INTO ht(time) VALUES ('2020-01-01');
+
+-- NO ACTION
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+
+-- ON UPDATE NO ACTION
+BEGIN;
+INSERT INTO fk_no_action(fk_no_action) VALUES ('fk_no_action');
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+-- should error
+UPDATE fk_no_action SET fk_no_action = 'fk_no_action_updated';
+ROLLBACK;
+
+-- ON UPDATE NO ACTION with compression
+BEGIN;
+INSERT INTO fk_no_action(fk_no_action) VALUES ('fk_no_action');
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+-- should error
+UPDATE fk_no_action SET fk_no_action = 'fk_no_action_updated';
+ROLLBACK;
+
+-- ON DELETE NO ACTION
+BEGIN;
+INSERT INTO fk_no_action(fk_no_action) VALUES ('fk_no_action');
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+-- should error
+DELETE FROM fk_no_action;
+ROLLBACK;
+
+-- ON DELETE NO ACTION with compression
+BEGIN;
+INSERT INTO fk_no_action(fk_no_action) VALUES ('fk_no_action');
+INSERT INTO ht(time, fk_no_action) VALUES ('2020-01-01', 'fk_no_action');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+-- should error
+DELETE FROM fk_no_action;
+ROLLBACK;
+
+-- RESTRICT
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+
+-- ON UPDATE RESTRICT
+BEGIN;
+INSERT INTO fk_restrict(fk_restrict) VALUES ('fk_restrict');
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+-- should error
+UPDATE fk_restrict SET fk_restrict = 'fk_restrict_updated';
+ROLLBACK;
+
+-- ON UPDATE RESTRICT with compression
+BEGIN;
+INSERT INTO fk_restrict(fk_restrict) VALUES ('fk_restrict');
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+-- should error
+UPDATE fk_restrict SET fk_restrict = 'fk_restrict_updated';
+ROLLBACK;
+
+-- ON DELETE RESTRICT
+BEGIN;
+INSERT INTO fk_restrict(fk_restrict) VALUES ('fk_restrict');
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+-- should error
+DELETE FROM fk_restrict;
+ROLLBACK;
+
+-- ON DELETE RESTRICT with compression
+BEGIN;
+INSERT INTO fk_restrict(fk_restrict) VALUES ('fk_restrict');
+INSERT INTO ht(time, fk_restrict) VALUES ('2020-01-01', 'fk_restrict');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+-- should error
+DELETE FROM fk_restrict;
+ROLLBACK;
+
+-- CASCADE
+
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+
+-- ON UPDATE CASCADE
+BEGIN;
+INSERT INTO fk_cascade(fk_cascade) VALUES ('fk_cascade');
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+-- should cascade
+UPDATE fk_cascade SET fk_cascade = 'fk_cascade_updated';
+SELECT * FROM ht;
+ROLLBACK;
+
+-- ON UPDATE CASCADE with compression
+BEGIN;
+INSERT INTO fk_cascade(fk_cascade) VALUES ('fk_cascade');
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+-- should cascade
+UPDATE fk_cascade SET fk_cascade = 'fk_cascade_updated';
+SELECT * FROM ht;
+EXPLAIN (analyze, costs off, timing off, summary off) SELECT * FROM ht;
+ROLLBACK;
+
+-- ON DELETE CASCADE
+BEGIN;
+INSERT INTO fk_cascade(fk_cascade) VALUES ('fk_cascade');
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+-- should cascade
+DELETE FROM fk_cascade;
+SELECT * FROM ht;
+ROLLBACK;
+
+-- ON DELETE CASCADE with compression
+BEGIN;
+INSERT INTO fk_cascade(fk_cascade) VALUES ('fk_cascade');
+INSERT INTO ht(time, fk_cascade) VALUES ('2020-01-01', 'fk_cascade');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+-- should cascade
+DELETE FROM fk_cascade;
+SELECT * FROM ht;
+EXPLAIN (analyze, costs off, timing off, summary off) SELECT * FROM ht;
+ROLLBACK;
+
+-- SET NULL
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+
+-- ON UPDATE SET NULL
+BEGIN;
+INSERT INTO fk_set_null(fk_set_null) VALUES ('fk_set_null');
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+-- should set column to null
+UPDATE fk_set_null SET fk_set_null = 'fk_set_null_updated';
+SELECT * FROM ht;
+ROLLBACK;
+
+-- ON UPDATE SET NULL with compression
+BEGIN;
+INSERT INTO fk_set_null(fk_set_null) VALUES ('fk_set_null');
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+-- should set column to null
+UPDATE fk_set_null SET fk_set_null = 'fk_set_null_updated';
+SELECT * FROM ht;
+EXPLAIN (analyze, costs off, timing off, summary off) SELECT * FROM ht;
+ROLLBACK;
+
+-- ON DELETE SET NULL
+BEGIN;
+INSERT INTO fk_set_null(fk_set_null) VALUES ('fk_set_null');
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+-- should set column to null
+DELETE FROM fk_set_null;
+SELECT * FROM ht;
+ROLLBACK;
+
+-- ON DELETE SET NULL with compression
+BEGIN;
+INSERT INTO fk_set_null(fk_set_null) VALUES ('fk_set_null');
+INSERT INTO ht(time, fk_set_null) VALUES ('2020-01-01', 'fk_set_null');
+SELECT count(compress_chunk(ch)) FROM show_chunks('ht') ch;
+-- should set column to null
+DELETE FROM fk_set_null;
+SELECT * FROM ht;
+EXPLAIN (analyze, costs off, timing off, summary off) SELECT * FROM ht;
+ROLLBACK;
+
+-- SET DEFAULT
+-- should fail with foreign key violation
+INSERT INTO ht(time, fk_set_default) VALUES ('2020-01-01', 'fk_set_default');
+
+-- set default is not supported for hypertables so these will fail
+BEGIN;
+INSERT INTO fk_set_default(fk_set_default) VALUES ('fk_set_default');
+INSERT INTO ht(time, fk_set_default) VALUES ('2020-01-01', 'fk_set_default');
+SELECT * FROM ht;
+UPDATE fk_set_default SET fk_set_default = 'fk_set_default_updated' WHERE fk_set_default = 'fk_set_default';
+ROLLBACK;
+BEGIN;
+INSERT INTO fk_set_default(fk_set_default) VALUES ('fk_set_default');
+INSERT INTO ht(time, fk_set_default) VALUES ('2020-01-01', 'fk_set_default');
+SELECT * FROM ht;
+DELETE FROM fk_set_default WHERE fk_set_default = 'fk_set_default';
+ROLLBACK;
+