-- 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 OR REPLACE FUNCTION part_func(id TEXT) RETURNS INTEGER LANGUAGE PLPGSQL IMMUTABLE AS $BODY$ DECLARE retval INTEGER; BEGIN retval := CAST(id AS INTEGER); RETURN retval; END $BODY$; -- test null handling \set ON_ERROR_STOP 0 CREATE TABLE n(); SELECT create_hypertable('n',NULL::_timescaledb_internal.dimension_info); SELECT add_dimension('n',NULL::_timescaledb_internal.dimension_info); \set ON_ERROR_STOP 1 SELECT by_range('id'); SELECT by_range('id', partition_func => 'part_func'); SELECT by_range('id', '1 week'::interval); SELECT by_range('id', '1 week'::interval, 'part_func'::regproc); SELECT by_hash('id', 3); SELECT by_hash('id', 3, partition_func => 'part_func'); -- Check if we handle the varlength of the datatype properly SELECT * FROM by_range('id') WHERE by_range IS NOT NULL; SELECT * FROM by_range('id', partition_func => 'part_func') WHERE by_range IS NOT NULL; SELECT * FROM by_hash('id', 3) WHERE by_hash IS NOT NULL; SELECT * FROM by_hash('id', 3, partition_func => 'part_func') WHERE by_hash IS NOT NULL; \set ON_ERROR_STOP 0 SELECT 'hash//id//3//-'::_timescaledb_internal.dimension_info; SELECT by_range(NULL::name); SELECT by_hash(NULL::name, 3); \set ON_ERROR_STOP 1 -- Validate generalized hypertable for smallint CREATE TABLE test_table_smallint(id SMALLINT, device INTEGER, time TIMESTAMPTZ); SELECT create_hypertable('test_table_smallint', by_range('id')); -- default interval SELECT integer_interval FROM timescaledb_information.dimensions WHERE hypertable_name = 'test_table_smallint'; -- Add data with default partition (10000) INSERT INTO test_table_smallint VALUES (1, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_smallint VALUES (9999, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_smallint VALUES (10000, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_smallint VALUES (20000, 10, '01-01-2023 11:00'::TIMESTAMPTZ); -- Number of chunks SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name='test_table_smallint'; -- Validate generalized hypertable for int CREATE TABLE test_table_int(id INTEGER, device INTEGER, time TIMESTAMPTZ); SELECT create_hypertable('test_table_int', by_range('id')); -- Default interval SELECT integer_interval FROM timescaledb_information.dimensions WHERE hypertable_name = 'test_table_int'; -- Add data INSERT INTO test_table_int VALUES (1, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_int VALUES (99999, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_int VALUES (100000, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_int VALUES (200000, 10, '01-01-2023 11:00'::TIMESTAMPTZ); -- Number of chunks SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name='test_table_int'; -- Validate generalized hypertable for bigint CREATE TABLE test_table_bigint(id BIGINT, device INTEGER, time TIMESTAMPTZ); SELECT create_hypertable('test_table_bigint', by_range('id')); -- Default interval SELECT integer_interval FROM timescaledb_information.dimensions WHERE hypertable_name = 'test_table_bigint'; -- Add data INSERT INTO test_table_bigint VALUES (1, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_bigint VALUES (999999, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_bigint VALUES (1000000, 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_bigint VALUES (2000000, 10, '01-01-2023 11:00'::TIMESTAMPTZ); -- Number of chunks SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name='test_table_bigint'; DROP TABLE test_table_smallint; DROP TABLE test_table_int; DROP TABLE test_table_bigint; -- Create hypertable with SERIAL column CREATE TABLE jobs_serial (job_id SERIAL, device_id INTEGER, start_time TIMESTAMPTZ, end_time TIMESTAMPTZ, PRIMARY KEY (job_id)); SELECT create_hypertable('jobs_serial', by_range('job_id', partition_interval => 30)); -- Insert data INSERT INTO jobs_serial (device_id, start_time, end_time) SELECT abs(timestamp_hash(t::timestamp)) % 10, t, t + INTERVAL '1 day' FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-08 1:00':: TIMESTAMPTZ,'1 hour')t; -- Verify chunk pruning EXPLAIN VERBOSE SELECT * FROM jobs_serial WHERE job_id < 30; EXPLAIN VERBOSE SELECT * FROM jobs_serial WHERE job_id >= 30 AND job_id < 90; EXPLAIN VERBOSE SELECT * FROM jobs_serial WHERE job_id > 90; -- Update rows UPDATE jobs_serial SET end_time = end_time + INTERVAL '1 hour' where job_id = 1; UPDATE jobs_serial SET end_time = end_time + INTERVAL '1 hour' where job_id = 30; UPDATE jobs_serial SET end_time = end_time + INTERVAL '1 hour' where job_id = 90; SELECT start_time, end_time FROM jobs_serial WHERE job_id = 1; SELECT start_time, end_time FROM jobs_serial WHERE job_id = 30; SELECT start_time, end_time FROM jobs_serial WHERE job_id = 90; -- Test delete rows -- Existing tuple counts. We saves these and compare with the values -- after running the delete. CREATE TABLE counts AS SELECT (SELECT count(*) FROM jobs_serial) AS total_count, (SELECT count(*) FROM jobs_serial WHERE job_id < 10) AS remove_count; -- Perform the delete DELETE FROM jobs_serial WHERE job_id < 10; -- Ensure only the intended tuples are deleted. The two counts should be equal. SELECT (SELECT total_count FROM counts) - (SELECT count(*) FROM jobs_serial) AS total_removed, (SELECT remove_count FROM counts) - (SELECT count(*) FROM jobs_serial WHERE job_id < 10) AS matching_removed; DROP TABLE jobs_serial; DROP TABLE counts; -- Create and validate hypertable with BIGSERIAL column CREATE TABLE jobs_big_serial (job_id BIGSERIAL, device_id INTEGER, start_time TIMESTAMPTZ, end_time TIMESTAMPTZ, PRIMARY KEY (job_id)); SELECT create_hypertable('jobs_big_serial', by_range('job_id', 100)); -- Insert data INSERT INTO jobs_big_serial (device_id, start_time, end_time) SELECT abs(timestamp_hash(t::timestamp)) % 10, t, t + INTERVAL '1 day' FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-08 1:00'::TIMESTAMPTZ,'30 mins')t; -- Verify #chunks SELECT count(*) FROM timescaledb_information.chunks; -- Get current sequence and verify updating sequence SELECT currval(pg_get_serial_sequence('jobs_big_serial', 'job_id')); -- Update sequence value to 500 SELECT setval(pg_get_serial_sequence('jobs_big_serial', 'job_id'), 500, false); -- Insert few rows and verify that the next sequence starts from 500 INSERT INTO jobs_big_serial (device_id, start_time, end_time) SELECT abs(timestamp_hash(t::timestamp)) % 10, t, t + INTERVAL '1 day' FROM generate_series('2018-03-09 1:00'::TIMESTAMPTZ, '2018-03-10 1:00'::TIMESTAMPTZ,'30 mins')t; -- No data should exist for job_id >= 290 to job_id < 500 SELECT count(*) FROM jobs_big_serial WHERE job_id >= 290 AND job_id < 500; -- The new rows should be added with job_id > 500 SELECT count(*) from jobs_big_serial WHERE job_id > 500; -- Verify show_chunks API SELECT show_chunks('jobs_big_serial', older_than => 100); SELECT show_chunks('jobs_big_serial', newer_than => 200, older_than => 300); SELECT show_chunks('jobs_big_serial', newer_than => 500); -- Verify drop_chunks API SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name = 'jobs_big_serial'; SELECT drop_chunks('jobs_big_serial', newer_than => 500); SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name = 'jobs_big_serial'; SELECT drop_chunks('jobs_big_serial', newer_than => 200, older_than => 300); SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name = 'jobs_big_serial'; DROP TABLE jobs_big_serial; -- Verify partition function CREATE TABLE test_table_int(id TEXT, device INTEGER, time TIMESTAMPTZ); SELECT create_hypertable('test_table_int', by_range('id', 10, partition_func => 'part_func')); INSERT INTO test_table_int VALUES('1', 1, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_int VALUES('10', 10, '01-01-2023 11:00'::TIMESTAMPTZ); INSERT INTO test_table_int VALUES('29', 100, '01-01-2023 11:00'::TIMESTAMPTZ); SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name = 'test_table_int'; DROP TABLE test_table_int; DROP FUNCTION part_func; -- Migrate data CREATE TABLE test_table_int(id INTEGER, device INTEGER, time TIMESTAMPTZ); INSERT INTO test_table_int SELECT t, t%10, '01-01-2023 11:00'::TIMESTAMPTZ FROM generate_series(1, 50, 1) t; SELECT create_hypertable('test_table_int', by_range('id', 10), migrate_data => true); -- Show default indexes created for hypertables. SELECT indexname FROM pg_indexes WHERE tablename = 'test_table_int'; SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name = 'test_table_int'; DROP TABLE test_table_int; -- create_hypertable without default indexes CREATE TABLE test_table_int(id INTEGER, device INTEGER, time TIMESTAMPTZ); SELECT create_hypertable('test_table_int', by_range('id', 10), create_default_indexes => false); SELECT indexname FROM pg_indexes WHERE tablename = 'test_table_int'; DROP TABLE test_table_int; -- if_not_exists CREATE TABLE test_table_int(id INTEGER, device INTEGER, time TIMESTAMPTZ); SELECT create_hypertable('test_table_int', by_range('id', 10)); -- No error when if_not_exists => true SELECT create_hypertable('test_table_int', by_range('id', 10), if_not_exists => true); SELECT * FROM _timescaledb_functions.get_create_command('test_table_int'); \set ON_ERROR_STOP 0 -- Should throw an error when if_not_exists is not set SELECT create_hypertable('test_table_int', by_range('id', 10)); -- Should error out when hash partitioning is used as the main partitioning scheme SELECT create_hypertable('test_table_int', by_hash('device', number_partitions => 2)); \set ON_ERROR_STOP 1 DROP TABLE test_table_int; -- Add dimension CREATE TABLE test_table_int(id INTEGER, device INTEGER, time TIMESTAMPTZ); SELECT create_hypertable('test_table_int', by_range('id', 10), migrate_data => true); INSERT INTO test_table_int SELECT t, t%10, '01-01-2023 11:00'::TIMESTAMPTZ FROM generate_series(1, 50, 1) t; -- adding a space dimension via "by_hash" should work SELECT add_dimension('test_table_int', by_hash('device', number_partitions => 2)); SELECT hypertable_name, dimension_number, column_name FROM timescaledb_information.dimensions WHERE hypertable_name = 'test_table_int'; SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name='test_table_int'; SELECT set_partitioning_interval('test_table_int', 5, 'id'); SELECT set_number_partitions('test_table_int', 3, 'device'); SELECT integer_interval, num_partitions FROM timescaledb_information.dimensions WHERE column_name in ('id', 'device'); DROP TABLE test_table_int; -- Hypertable with time dimension using new API CREATE TABLE test_time(time TIMESTAMP NOT NULL, device INT, temp FLOAT); SELECT create_hypertable('test_time', by_range('time')); -- Default interval SELECT time_interval FROM timescaledb_information.dimensions WHERE hypertable_name = 'test_time'; INSERT INTO test_time SELECT t, (abs(timestamp_hash(t::timestamp)) % 10) + 1, 0.10 FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-08 1:00', '1 hour') t; SELECT count(*) FROM timescaledb_information.chunks WHERE hypertable_name='test_time'; SELECT add_dimension('test_time', by_range('device', partition_interval => 2)); SELECT hypertable_name, dimension_number, column_name FROM timescaledb_information.dimensions WHERE hypertable_name = 'test_time'; SELECT set_partitioning_interval('test_time', INTERVAL '1 day', 'time'); SELECT time_interval FROM timescaledb_information.dimensions WHERE hypertable_name = 'test_time' AND column_name = 'time'; DROP TABLE test_time;