mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-23 22:41:34 +08:00
This change is part of an effort to create a consistent way of dealing with metadata catalog updates, which is currently a mix of C API and INSERT/UPDATE/DELETE statements from SQL code. This mix makes catalog handling unnecessarily complex as there are multiple ways to update metadata, increasing the risk of security issues with publically exposed SQL functions. It also complicates things like cache invalidation, requiring different mechanisms for C and SQL code. Catalog updates from SQL code require triggers on metadata tables for cache invalidation that do not work with native catalog updates. The creation of chunks has been particularly messy in this regard, making the code hard to follow. Especially the handling of a chunk's constraints, where dimensional and other constraints were handled differently. With this change, constraint handling is now consistent across constraint types with a single API for updating metadata. Reduce memory usage for out-of-order inserts The chunk_result_relation_info should be put on the chunk memory context. This will cause the rri constraint expr to also go onto that context and be correctly freed when the chunk insert state is destroyed.
331 lines
11 KiB
SQL
331 lines
11 KiB
SQL
CREATE TABLE hyper (
|
|
time BIGINT NOT NULL,
|
|
device_id TEXT NOT NULL,
|
|
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
|
);
|
|
|
|
|
|
SELECT * FROM create_hypertable('hyper', 'time', chunk_time_interval => 10);
|
|
|
|
--check and not-null constraints are inherited through regular inheritance.
|
|
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 9);
|
|
|
|
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, NULL, 11);
|
|
|
|
\set ON_ERROR_STOP 1
|
|
|
|
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
|
|
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
|
|
----------------------- UNIQUE CONSTRAINTS ------------------
|
|
|
|
CREATE TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name (
|
|
time BIGINT NOT NULL UNIQUE,
|
|
device_id TEXT NOT NULL,
|
|
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
|
);
|
|
|
|
|
|
SELECT * FROM create_hypertable('hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name', 'time', chunk_time_interval => 10);
|
|
|
|
INSERT INTO hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
|
|
INSERT INTO hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name(time, device_id,sensor_1) VALUES
|
|
(1257987800000000000, 'dev2', 11);
|
|
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Show constraints on main tables
|
|
SELECT * FROM _timescaledb_catalog.chunk_constraint;
|
|
SELECT * FROM test.show_constraints('hyper');
|
|
SELECT * FROM test.show_constraints('hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name');
|
|
--should have unique constraint not just unique index
|
|
SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk');
|
|
|
|
ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name DROP CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key;
|
|
-- The constraint should have been removed from the chunk as well
|
|
SELECT * FROM _timescaledb_catalog.chunk_constraint;
|
|
SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk');
|
|
|
|
--uniqueness not enforced
|
|
INSERT INTO hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev3', 11);
|
|
|
|
--shouldn't be able to create constraint
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name ADD CONSTRAINT hyper_unique_time_key UNIQUE (time);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DELETE FROM hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name WHERE device_id = 'dev3';
|
|
|
|
-- Try multi-alter table statement with a constraint without a name
|
|
ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name
|
|
ADD CHECK (time > 0),
|
|
ADD UNIQUE (time);
|
|
|
|
SELECT * FROM _timescaledb_catalog.chunk_constraint;
|
|
SELECT * FROM test.show_constraints('hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name');
|
|
SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk');
|
|
|
|
ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name
|
|
DROP CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key,
|
|
DROP CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooo_time_check;
|
|
|
|
SELECT * FROM _timescaledb_catalog.chunk_constraint;
|
|
SELECT * FROM test.show_constraints('hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name');
|
|
SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk');
|
|
|
|
CREATE UNIQUE INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx
|
|
ON hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name (time);
|
|
|
|
\set ON_ERROR_STOP 0
|
|
-- Try adding constraint using existing index
|
|
ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name
|
|
ADD CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key UNIQUE
|
|
USING INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx;
|
|
\set ON_ERROR_STOP 1
|
|
DROP INDEX hyper_unique_with_looooooooooooooooooooooooooooooooo_time_idx;
|
|
|
|
--now can create
|
|
ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name
|
|
ADD CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key UNIQUE (time);
|
|
SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_2_4_chunk');
|
|
|
|
--test adding constraint with same name to different table -- should fail
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE hyper
|
|
ADD CONSTRAINT hyper_unique_with_looooooooooooooooooooooooooooooooooo_time_key UNIQUE (time);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
--uniquness violation fails
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name(time, device_id,sensor_1)
|
|
VALUES (1257987700000000000, 'dev2', 11);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
--cannot create unique constraint on non-partition column
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE hyper_unique_with_looooooooooooooooooooooooooooooooooooong_name
|
|
ADD CONSTRAINT hyper_unique_invalid UNIQUE (device_id);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
|
|
----------------------- PRIMARY KEY ------------------
|
|
|
|
CREATE TABLE hyper_pk (
|
|
time BIGINT NOT NULL PRIMARY KEY,
|
|
device_id TEXT NOT NULL,
|
|
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
|
);
|
|
|
|
|
|
SELECT * FROM create_hypertable('hyper_pk', 'time', chunk_time_interval => 10);
|
|
|
|
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
--should have unique constraint not just unique index
|
|
SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_3_6_chunk');
|
|
|
|
ALTER TABLE hyper_pk DROP CONSTRAINT hyper_pk_pkey;
|
|
SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_3_6_chunk');
|
|
|
|
--uniqueness not enforced
|
|
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev3', 11);
|
|
|
|
|
|
--shouldn't be able to create pk
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_pkey PRIMARY KEY (time);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DELETE FROM hyper_pk WHERE device_id = 'dev3';
|
|
|
|
--cannot create pk constraint on non-partition column
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_invalid PRIMARY KEY (device_id);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
--now can create
|
|
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_pkey PRIMARY KEY (time);
|
|
SELECT * FROM test.show_constraints('_timescaledb_internal._hyper_3_6_chunk');
|
|
|
|
--test adding constraint with same name to different table -- should fail
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE hyper ADD CONSTRAINT hyper_pk_pkey UNIQUE (time);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
--uniquness violation fails
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
|
|
----------------------- FOREIGN KEY ------------------
|
|
|
|
CREATE TABLE devices(
|
|
device_id TEXT NOT NULL,
|
|
PRIMARY KEY (device_id)
|
|
);
|
|
|
|
CREATE TABLE hyper_fk (
|
|
time BIGINT NOT NULL PRIMARY KEY,
|
|
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
|
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
|
);
|
|
|
|
|
|
SELECT * FROM create_hypertable('hyper_fk', 'time', chunk_time_interval => 10);
|
|
|
|
--fail fk constraint
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
INSERT INTO devices VALUES ('dev2');
|
|
|
|
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
|
|
--delete should fail
|
|
\set ON_ERROR_STOP 0
|
|
DELETE FROM devices;
|
|
\set ON_ERROR_STOP 1
|
|
|
|
ALTER TABLE hyper_fk DROP CONSTRAINT hyper_fk_device_id_fkey;
|
|
|
|
--should now be able to add non-fk rows
|
|
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
|
(1257987700000000001, 'dev3', 11);
|
|
|
|
--can't add fk because of dev3 row
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE hyper_fk ADD CONSTRAINT hyper_fk_device_id_fkey
|
|
FOREIGN KEY (device_id) REFERENCES devices(device_id);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DELETE FROM hyper_fk WHERE device_id = 'dev3';
|
|
|
|
ALTER TABLE hyper_fk ADD CONSTRAINT hyper_fk_device_id_fkey
|
|
FOREIGN KEY (device_id) REFERENCES devices(device_id);
|
|
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
|
(1257987700000000002, 'dev3', 11);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
----------------------- FOREIGN KEY INTO A HYPERTABLE ------------------
|
|
|
|
|
|
--FOREIGN KEY references into a hypertable are currently broken.
|
|
--The referencing table will never find the corresponding row in the hypertable
|
|
--since it will only search the parent. Thus any insert will result in an ERROR
|
|
--TODO: block such foreign keys or fix. (Hard to block on create table so punting for now)
|
|
CREATE TABLE hyper_for_ref (
|
|
time BIGINT NOT NULL PRIMARY KEY,
|
|
device_id TEXT NOT NULL,
|
|
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
|
);
|
|
|
|
SELECT * FROM create_hypertable('hyper_for_ref', 'time', chunk_time_interval => 10);
|
|
|
|
\set ON_ERROR_STOP 0
|
|
CREATE TABLE referrer (
|
|
time BIGINT NOT NULL REFERENCES hyper_for_ref(time)
|
|
);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
CREATE TABLE referrer2 (
|
|
time BIGINT NOT NULL
|
|
);
|
|
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE referrer2 ADD CONSTRAINT hyper_fk_device_id_fkey
|
|
FOREIGN KEY (time) REFERENCES hyper_for_ref(time);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
----------------------- EXCLUSION CONSTRAINT ------------------
|
|
|
|
CREATE TABLE hyper_ex (
|
|
time BIGINT,
|
|
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
|
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10),
|
|
canceled boolean DEFAULT false,
|
|
EXCLUDE USING btree (
|
|
time WITH =, device_id WITH =
|
|
) WHERE (not canceled)
|
|
);
|
|
|
|
SELECT * FROM create_hypertable('hyper_ex', 'time', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
|
|
|
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 11);
|
|
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 12);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
ALTER TABLE hyper_ex DROP CONSTRAINT hyper_ex_time_device_id_excl;
|
|
|
|
--can now add
|
|
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 12);
|
|
|
|
--cannot add because of conflicts
|
|
\set ON_ERROR_STOP 0
|
|
ALTER TABLE hyper_ex ADD CONSTRAINT hyper_ex_time_device_id_excl
|
|
EXCLUDE USING btree (
|
|
time WITH =, device_id WITH =
|
|
) WHERE (not canceled)
|
|
;
|
|
\set ON_ERROR_STOP 1
|
|
|
|
DELETE FROM hyper_ex WHERE sensor_1 = 12;
|
|
|
|
ALTER TABLE hyper_ex ADD CONSTRAINT hyper_ex_time_device_id_excl
|
|
EXCLUDE USING btree (
|
|
time WITH =, device_id WITH =
|
|
) WHERE (not canceled)
|
|
;
|
|
|
|
\set ON_ERROR_STOP 0
|
|
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
|
(1257987700000000000, 'dev2', 12);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
|
|
--cannot add exclusion constraint without partition key.
|
|
CREATE TABLE hyper_ex_invalid (
|
|
time BIGINT,
|
|
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
|
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10),
|
|
canceled boolean DEFAULT false,
|
|
EXCLUDE USING btree (
|
|
device_id WITH =
|
|
) WHERE (not canceled)
|
|
);
|
|
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM create_hypertable('hyper_ex_invalid', 'time', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
|
\set ON_ERROR_STOP 1
|