-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
CREATE TABLE metrics(time timestamptz not null, device text, value float);
SELECT create_hypertable('metrics','time');
  create_hypertable   
----------------------
 (1,public,metrics,t)

ALTER TABLE metrics SET (tsdb.compress, tsdb.compress_segmentby='device', tsdb.compress_orderby='time');
-- test all variations without data
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_pk PRIMARY KEY(time);
ROLLBACK;
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_pk PRIMARY KEY(time, device);
ROLLBACK;
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_pk UNIQUE(time);
ROLLBACK;
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_pk UNIQUE(time, device);
ROLLBACK;
BEGIN;
CREATE UNIQUE INDEX metrics_pk ON metrics(time);
ROLLBACK;
BEGIN;
CREATE UNIQUE INDEX metrics_pk ON metrics(time,device);
ROLLBACK;
-- test all variations with data but no conflict
INSERT INTO metrics SELECT '2025-01-01', 'd1', 0.1;
SELECT count(compress_chunk(ch)) FROM show_chunks('metrics') ch;
 count 
-------
     1

\set ON_ERROR_STOP 0
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_pk PRIMARY KEY(time);
INSERT INTO metrics SELECT '2025-01-02', 'd1', 0.1;
INSERT INTO metrics SELECT '2025-01-01', 'd1', 0.1;
ERROR:  duplicate key value violates unique constraint "1_1_metrics_pk"
ROLLBACK;
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_pk PRIMARY KEY(time, device);
INSERT INTO metrics SELECT '2025-01-02', 'd1', 0.1;
INSERT INTO metrics SELECT '2025-01-01', 'd1', 0.1;
ERROR:  duplicate key value violates unique constraint "1_3_metrics_pk"
ROLLBACK;
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_pk UNIQUE(time);
INSERT INTO metrics SELECT '2025-01-02', 'd1', 0.1;
INSERT INTO metrics SELECT '2025-01-01', 'd1', 0.1;
ERROR:  duplicate key value violates unique constraint "1_5_metrics_pk"
ROLLBACK;
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_pk UNIQUE(time, device);
INSERT INTO metrics SELECT '2025-01-02', 'd1', 0.1;
INSERT INTO metrics SELECT '2025-01-01', 'd1', 0.1;
ERROR:  duplicate key value violates unique constraint "1_7_metrics_pk"
ROLLBACK;
BEGIN;
CREATE UNIQUE INDEX metrics_pk ON metrics(time);
INSERT INTO metrics SELECT '2025-01-02', 'd1', 0.1;
INSERT INTO metrics SELECT '2025-01-01', 'd1', 0.1;
ERROR:  duplicate key value violates unique constraint "_hyper_1_1_chunk_metrics_pk"
ROLLBACK;
BEGIN;
CREATE UNIQUE INDEX metrics_pk ON metrics(time,device);
INSERT INTO metrics SELECT '2025-01-02', 'd1', 0.1;
INSERT INTO metrics SELECT '2025-01-01', 'd1', 0.1;
ERROR:  duplicate key value violates unique constraint "_hyper_1_1_chunk_metrics_pk"
ROLLBACK;
\set ON_ERROR_STOP 0
-- test with conflict across uncompressed/compressed
INSERT INTO metrics SELECT '2025-01-01', 'd1', 0.1;
\set ON_ERROR_STOP 0
ALTER TABLE metrics ADD CONSTRAINT metrics_pk PRIMARY KEY(time);
ERROR:  duplicate key value violates unique constraint
ALTER TABLE metrics ADD CONSTRAINT metrics_pk PRIMARY KEY(time, device);
ERROR:  duplicate key value violates unique constraint
ALTER TABLE metrics ADD CONSTRAINT metrics_pk UNIQUE(time);
ERROR:  duplicate key value violates unique constraint
ALTER TABLE metrics ADD CONSTRAINT metrics_pk UNIQUE(time, device);
ERROR:  duplicate key value violates unique constraint
CREATE UNIQUE INDEX metrics_pk ON metrics(time);
ERROR:  duplicate key value violates unique constraint
CREATE UNIQUE INDEX metrics_pk ON metrics(time,device);
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
-- test with conflict in compressed
SELECT count(compress_chunk(ch)) FROM show_chunks('metrics') ch;
 count 
-------
     1

\set ON_ERROR_STOP 0
ALTER TABLE metrics ADD CONSTRAINT metrics_pk PRIMARY KEY(time);
ERROR:  duplicate key value violates unique constraint
ALTER TABLE metrics ADD CONSTRAINT metrics_pk PRIMARY KEY(time, device);
ERROR:  duplicate key value violates unique constraint
ALTER TABLE metrics ADD CONSTRAINT metrics_pk UNIQUE(time);
ERROR:  duplicate key value violates unique constraint
ALTER TABLE metrics ADD CONSTRAINT metrics_pk UNIQUE(time, device);
ERROR:  duplicate key value violates unique constraint
CREATE UNIQUE INDEX metrics_pk ON metrics(time);
ERROR:  duplicate key value violates unique constraint
CREATE UNIQUE INDEX metrics_pk ON metrics(time,device);
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
DROP TABLE metrics;
-- test NULL behaviour
CREATE TABLE dist_null(time timestamptz not null, device text, value float);
SELECT table_name FROM create_hypertable('dist_null','time',chunk_time_interval:='1 year'::interval);
 table_name 
------------
 dist_null

ALTER TABLE dist_null SET (tsdb.compress, tsdb.compress_segmentby='device', tsdb.compress_orderby='time');
-- test behaviour without compression
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS DISTINCT;
DROP INDEX dist_null_idx;
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS NOT DISTINCT;
ERROR:  could not create unique index "_hyper_3_9_chunk_dist_null_idx"
\set ON_ERROR_STOP 1
ROLLBACK;
-- test behaviour with conflict in compressed
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
SELECT count(compress_chunk(ch)) FROM show_chunks('dist_null') ch;
 count 
-------
     1

CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS DISTINCT;
DROP INDEX dist_null_idx;
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS NOT DISTINCT;
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
ROLLBACK;
-- test behaviour with conflict between compressed/uncompressed
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
SELECT count(compress_chunk(ch)) FROM show_chunks('dist_null') ch;
 count 
-------
     1

INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS DISTINCT;
DROP INDEX dist_null_idx;
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS NOT DISTINCT;
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
ROLLBACK;
-- test NULLs present but conflict in non-NULL tuples
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS DISTINCT;
ERROR:  could not create unique index "_hyper_3_14_chunk_dist_null_idx"
\set ON_ERROR_STOP 1
ROLLBACK;
-- test NULLs present but conflict in non-NULL tuples
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS NOT DISTINCT;
ERROR:  could not create unique index "_hyper_3_15_chunk_dist_null_idx"
\set ON_ERROR_STOP 1
ROLLBACK;
-- test NULLs present but conflict in non-NULL tuples (full compressed)
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
SELECT count(compress_chunk(ch)) FROM show_chunks('dist_null') ch;
 count 
-------
     1

\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS DISTINCT;
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
ROLLBACK;
-- test NULLs present but conflict in non-NULL tuples (full compressed)
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
SELECT count(compress_chunk(ch)) FROM show_chunks('dist_null') ch;
 count 
-------
     1

\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS NOT DISTINCT;
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
ROLLBACK;
-- test NULLs present but conflict in non-NULL tuples (partially compressed)
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
SELECT count(compress_chunk(ch)) FROM show_chunks('dist_null') ch;
 count 
-------
     1

INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS DISTINCT;
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
ROLLBACK;
-- test NULLs present but conflict in non-NULL tuples (partially compressed)
BEGIN;
INSERT INTO dist_null SELECT '2025-01-01', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', NULL, 0.1;
INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
SELECT count(compress_chunk(ch)) FROM show_chunks('dist_null') ch;
 count 
-------
     1

INSERT INTO dist_null SELECT '2025-01-02', 'd', 0.1;
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX dist_null_idx ON dist_null(time,device) NULLS NOT DISTINCT;
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
ROLLBACK;
-- test foreign keys
-- hypertable referencing plain table
CREATE TABLE metrics(time timestamptz NOT NULL, device text, value float, PRIMARY KEY(device,time));
SELECT create_hypertable('metrics','time');
  create_hypertable   
----------------------
 (5,public,metrics,t)

INSERT INTO metrics SELECT '2025-01-01','d1',0.1;
INSERT INTO metrics SELECT '2025-01-01','d2',0.1;
CREATE TABLE device(device TEXT PRIMARY KEY);
INSERT INTO device SELECT 'd1';
CREATE TABLE events(time timestamptz, device text, data text);
INSERT INTO events SELECT '2025-02-01', 'd1', '';
\set ON_ERROR_STOP 0
ALTER TABLE metrics ADD CONSTRAINT device_fk FOREIGN KEY(device) REFERENCES device(device);
ERROR:  insert or update on table "metrics" violates foreign key constraint "device_fk"
ALTER TABLE events ADD CONSTRAINT events_fk FOREIGN KEY(device,time) REFERENCES metrics(device,time);
ERROR:  insert or update on table "events" violates foreign key constraint "events_fk"
\set ON_ERROR_STOP 1
BEGIN;
INSERT INTO device SELECT 'd2';
ALTER TABLE metrics ADD CONSTRAINT device_fk FOREIGN KEY(device) REFERENCES device(device);
INSERT INTO metrics SELECT '2025-03-01','d2',0.3;
\set ON_ERROR_STOP 0
INSERT INTO metrics SELECT '2025-03-01','d3',0.3;
ERROR:  insert or update on table "_hyper_5_25_chunk" violates foreign key constraint "25_11_device_fk"
\set ON_ERROR_STOP 1
ROLLBACK;
BEGIN;
INSERT INTO metrics SELECT '2025-02-01','d1',0.1;
ALTER TABLE events ADD CONSTRAINT events_fk FOREIGN KEY(device,time) REFERENCES metrics(device,time);
INSERT INTO events SELECT '2025-01-01','d1','';
\set ON_ERROR_STOP 0
INSERT INTO events SELECT '2025-03-01','d1','';
ERROR:  insert or update on table "events" violates foreign key constraint "events_fk"
\set ON_ERROR_STOP 1
ROLLBACK;
ALTER TABLE metrics SET (tsdb.compress, tsdb.compress_orderby='device,time DESC', tsdb.compress_segmentby='');
SELECT count(compress_chunk(ch)) FROM show_chunks('metrics') ch;
 count 
-------
     1

\set ON_ERROR_STOP 0
ALTER TABLE metrics ADD CONSTRAINT device_fk FOREIGN KEY(device) REFERENCES device(device);
ERROR:  insert or update on table "metrics" violates foreign key constraint "device_fk"
ALTER TABLE events ADD CONSTRAINT events_fk FOREIGN KEY(device,time) REFERENCES metrics(device,time);
ERROR:  insert or update on table "events" violates foreign key constraint "events_fk"
\set ON_ERROR_STOP 1
BEGIN;
INSERT INTO device SELECT 'd2';
ALTER TABLE metrics ADD CONSTRAINT device_fk FOREIGN KEY(device) REFERENCES device(device);
INSERT INTO metrics SELECT '2025-03-01','d2',0.3;
\set ON_ERROR_STOP 0
INSERT INTO metrics SELECT '2025-03-01','d3',0.3;
ERROR:  insert or update on table "_hyper_5_28_chunk" violates foreign key constraint "28_15_device_fk"
\set ON_ERROR_STOP 1
ROLLBACK;
BEGIN;
INSERT INTO metrics SELECT '2025-02-01','d1',0.1;
ALTER TABLE events ADD CONSTRAINT events_fk FOREIGN KEY(device,time) REFERENCES metrics(device,time);
INSERT INTO events SELECT '2025-01-01','d1','';
\set ON_ERROR_STOP 0
INSERT INTO events SELECT '2025-03-01','d1','';
ERROR:  insert or update on table "events" violates foreign key constraint "events_fk"
\set ON_ERROR_STOP 1
ROLLBACK;
DROP TABLE metrics CASCADE;
DROP TABLE device CASCADE;
DROP TABLE events CASCADE;
-- test CHECK constraints
CREATE TABLE metrics(time timestamptz not null, device text, value float);
SELECT create_hypertable('metrics','time');
  create_hypertable   
----------------------
 (7,public,metrics,t)

ALTER TABLE metrics SET (tsdb.compress, tsdb.compress_segmentby='device', tsdb.compress_orderby='time');
INSERT INTO metrics SELECT '2025-01-01','d1',1;
INSERT INTO metrics SELECT '2025-01-01','d1',NULL;
\set ON_ERROR_STOP 0
ALTER TABLE metrics ADD CONSTRAINT metrics_check CHECK (value > 9000);
ERROR:  check constraint "metrics_check" of relation "_hyper_7_30_chunk" is violated by some row
ALTER TABLE metrics ADD CONSTRAINT metrics_check CHECK (time > '2026-01-01' AND value > 0);
ERROR:  check constraint "metrics_check" of relation "_hyper_7_30_chunk" is violated by some row
\set ON_ERROR_STOP 1
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_check CHECK (value > 0.9);
ALTER TABLE metrics ADD CONSTRAINT metrics_check2 CHECK (time >= '2025-01-01' AND value > 0);
INSERT INTO metrics SELECT '2025-01-01','d1',2;
\set ON_ERROR_STOP 0
INSERT INTO metrics SELECT '2025-01-01','d1',0;
ERROR:  new row for relation "_hyper_7_30_chunk" violates check constraint "metrics_check"
\set ON_ERROR_STOP 1
ROLLBACK;
SELECT count(compress_chunk(ch)) FROM show_chunks('metrics') ch;
 count 
-------
     1

\set ON_ERROR_STOP 0
ALTER TABLE metrics ADD CONSTRAINT metrics_check CHECK (value > 9000);
ERROR:  check constraint "metrics_check" of relation "_hyper_7_30_chunk" is violated by some row
ALTER TABLE metrics ADD CONSTRAINT metrics_check CHECK (time > '2026-01-01' AND value > 0);
ERROR:  check constraint "metrics_check" of relation "_hyper_7_30_chunk" is violated by some row
\set ON_ERROR_STOP 1
BEGIN;
ALTER TABLE metrics ADD CONSTRAINT metrics_check CHECK (value > 0.9);
ALTER TABLE metrics ADD CONSTRAINT metrics_check2 CHECK (time >= '2025-01-01' AND value > 0);
INSERT INTO metrics SELECT '2025-01-01','d1',2;
\set ON_ERROR_STOP 0
INSERT INTO metrics SELECT '2025-01-01','d1',0;
ERROR:  new row for relation "_hyper_7_30_chunk" violates check constraint "metrics_check"
\set ON_ERROR_STOP 1
ROLLBACK;
DROP TABLE metrics CASCADE;
-- test adding columns and constraints together
CREATE TABLE metrics(time timestamptz not null, device text, value float);
SELECT create_hypertable('metrics','time');
  create_hypertable   
----------------------
 (9,public,metrics,t)

ALTER TABLE metrics SET (tsdb.compress, tsdb.compress_segmentby='device', tsdb.compress_orderby='time');
INSERT INTO metrics SELECT '2025-01-01','d1',1;
-- should fail in validation since the new column will be NULL for existing tuples
\set ON_ERROR_STOP 0
ALTER TABLE metrics ADD COLUMN c1 int CHECK ( c1 IS NOT NULL );
ERROR:  check constraint "metrics_c1_check" of relation "_hyper_9_32_chunk" is violated by some row
\set ON_ERROR_STOP 1
BEGIN;
-- should succeed since the CHECK constraint will succeed for NULL values
ALTER TABLE metrics ADD COLUMN c1 int CHECK (c1 <> 42);
INSERT INTO metrics SELECT '2025-01-01','d1',1,23;
\set ON_ERROR_STOP 0
INSERT INTO metrics SELECT '2025-01-01','d1',1,42;
ERROR:  new row for relation "_hyper_9_32_chunk" violates check constraint "metrics_c1_check"
\set ON_ERROR_STOP 1
ROLLBACK;
SELECT count(compress_chunk(ch)) FROM show_chunks('metrics') ch;
 count 
-------
     1

-- should fail in validation since the new column will be NULL for existing tuples
\set ON_ERROR_STOP 0
ALTER TABLE metrics ADD COLUMN c1 int CHECK ( c1 IS NOT NULL );
ERROR:  check constraint "metrics_c1_check" of relation "_hyper_9_32_chunk" is violated by some row
\set ON_ERROR_STOP 1
BEGIN;
-- should succeed since the CHECK constraint will succeed for NULL values
ALTER TABLE metrics ADD COLUMN c1 int CHECK (c1 <> 42);
INSERT INTO metrics SELECT '2025-01-01','d1',1,23;
\set ON_ERROR_STOP 0
INSERT INTO metrics SELECT '2025-01-01','d1',1,42;
ERROR:  new row for relation "_hyper_9_32_chunk" violates check constraint "metrics_c1_check"
\set ON_ERROR_STOP 1
ROLLBACK;
-- test unique expression indexes
BEGIN;
-- should succeed since no conflict
CREATE UNIQUE INDEX metrics_unique ON metrics(time, device, md5(value::text));
ROLLBACK;
BEGIN;
INSERT INTO metrics SELECT '2025-01-01','d1',1;
-- partially compressed chunk
-- should fail because there is conflict
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX metrics_unique ON metrics(time, device, md5(value::text));
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
ROLLBACK;
BEGIN;
INSERT INTO metrics SELECT '2025-01-01','d1',1;
SELECT count(compress_chunk(ch)) FROM show_chunks('metrics') ch;
 count 
-------
     1

-- fully compressed chunk
-- should fail because there is conflict
\set ON_ERROR_STOP 0
CREATE UNIQUE INDEX metrics_unique ON metrics(time, device, md5(value::text));
ERROR:  duplicate key value violates unique constraint
\set ON_ERROR_STOP 1
ROLLBACK;
