-- 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. \ir include/cluster_test_setup.sql BEGIN; SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; -- Show chunk indexes SELECT * FROM test.show_indexes('_timescaledb_internal._hyper_1_1_chunk'); SELECT * FROM test.show_indexes('_timescaledb_internal._hyper_1_2_chunk'); \set ON_ERROR_STOP 0 -- cannot reorder a if with no index of the first call SELECT reorder_chunk('_timescaledb_internal._hyper_1_2_chunk', verbose => TRUE); \set ON_ERROR_STOP 1 BEGIN; SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; -- reorder a chunk directly with an chunk index SELECT reorder_chunk('_timescaledb_internal._hyper_1_2_chunk', '_timescaledb_internal._hyper_1_2_chunk_cluster_test_time_idx', TRUE); BEGIN; SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; -- reorder a chunk without an index SELECT reorder_chunk('_timescaledb_internal._hyper_1_2_chunk', verbose => TRUE); BEGIN; SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; -- reorder a chunk directly with a hypertable index SELECT reorder_chunk('_timescaledb_internal._hyper_1_2_chunk', 'cluster_test_time_idx', TRUE); BEGIN; SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; \set ON_ERROR_STOP 0 -- other chunks in the hypertable are still not clustered SELECT reorder_chunk('_timescaledb_internal._hyper_1_1_chunk', verbose => TRUE); -- cannot cluster using another chunk's index SELECT reorder_chunk('_timescaledb_internal._hyper_1_2_chunk', '_timescaledb_internal._hyper_1_1_chunk_cluster_test_time_idx', TRUE); -- cannot reorder a hypertable SELECT reorder_chunk('cluster_test', 'cluster_test_time_idx', TRUE); -- cannot reorder NULL SELECT reorder_chunk(NULL, verbose => TRUE); -- cannot reorder within a transaction BEGIN; SELECT reorder_chunk('_timescaledb_internal._hyper_1_2_chunk', verbose => TRUE); END; \set ON_ERROR_STOP 1 BEGIN; SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; -- reorder puts things in the correct order SELECT ctid, time, temp, location, substring(value for 30), length(value) FROM _timescaledb_internal._hyper_1_1_chunk ORDER BY ctid; SELECT ctid, time, temp, location, substring(value for 30), length(value) FROM _timescaledb_internal._hyper_1_2_chunk ORDER BY ctid; -- can perform first reorder using a hypertable index (also test non-toast) SELECT reorder_chunk('_timescaledb_internal._hyper_1_1_chunk', 'cluster_test_time_idx', verbose => TRUE); BEGIN; SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; -- and this sets the clustered index correctly SELECT reorder_chunk('_timescaledb_internal._hyper_1_1_chunk', verbose => TRUE); BEGIN; SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; -- reorder puts things in the correct order SELECT ctid, time, temp, location, substring(value for 30), length(value) FROM _timescaledb_internal._hyper_1_1_chunk ORDER BY ctid; SELECT ctid, time, temp, location, substring(value for 30), length(value) FROM _timescaledb_internal._hyper_1_2_chunk ORDER BY ctid; -- can still use other index BEGIN; SET LOCAL enable_seqscan=false; SET LOCAL enable_indexscan=true; SET LOCAL enable_bitmapscan=false; EXPLAIN (costs off) SELECT * FROM cluster_test WHERE location < 6; SELECT time, temp, location, substring(value for 30), length(value) FROM cluster_test where location < 6 ORDER BY time; COMMIT; -- drop toast column ALTER TABLE cluster_test DROP COLUMN value; ALTER TABLE expected DROP COLUMN value; BEGIN; SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; SELECT reorder_chunk('_timescaledb_internal._hyper_1_1_chunk', verbose => TRUE); BEGIN; SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; -- reorder puts things in the correct order SELECT ctid, time, temp, location FROM _timescaledb_internal._hyper_1_1_chunk ORDER BY ctid; SELECT ctid, time, temp, location FROM _timescaledb_internal._hyper_1_2_chunk ORDER BY ctid; -- force an indexscan SET enable_seqscan=false; SET enable_indexscan=true; SET enable_bitmapscan=false; SELECT reorder_chunk('_timescaledb_internal._hyper_1_1_chunk', verbose => TRUE); BEGIN; SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', seqscan => true) \gexec SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', indexscan => true) \gexec SELECT * FROM ensure_scans_work_no_val('cluster_test', should_output => 'expected', bitmapscan => true) \gexec COMMIT; SET enable_seqscan=Default; SET enable_indexscan=Default; SET enable_bitmapscan=Default; -- reorder puts things in the correct order SELECT ctid, time, temp, location FROM _timescaledb_internal._hyper_1_1_chunk ORDER BY time; SELECT ctid, time, temp, location FROM _timescaledb_internal._hyper_1_2_chunk ORDER BY time; CREATE TABLE ct2(time INTEGER, val BIGINT); SELECT create_hypertable('ct2', 'time', chunk_time_interval => 5); INSERT INTO ct2 VALUES (10, 0), ( 2, 12), ( 0, 10), (14, 4), (11, 1), (12, 2), ( 3, 13), ( 1, 11), ( 4, 14), (13, 13); select show_chunks('ct2'); -- if we use someone elses index error \set ON_ERROR_STOP 0 SELECT reorder_chunk('_timescaledb_internal._hyper_2_3_chunk', 'cluster_test_time_idx', verbose => TRUE); SELECT reorder_chunk('_timescaledb_internal._hyper_2_3_chunk', '_timescaledb_internal._hyper_1_2_chunk_cluster_test_time_idx', verbose => TRUE); \set ON_ERROR_STOP 1 -- if the hypertable has a CLUSTERed index we can use it CLUSTER ct2 USING ct2_time_idx; SELECT reorder_chunk('_timescaledb_internal._hyper_2_3_chunk', verbose => TRUE); -- if we drop a CLUSTERed index we still fail correctly DROP INDEX ct2_time_idx; \set ON_ERROR_STOP 0 SELECT reorder_chunk('_timescaledb_internal._hyper_2_3_chunk', verbose => TRUE); \set ON_ERROR_STOP 1 -- but re-create for future tests CREATE INDEX ct2_time_idx ON ct2(time DESC); CLUSTER ct2 USING ct2_time_idx; -- deleted chunks are removed correctly DELETE FROM ct2 where time < 2 OR val < 2; SELECT reorder_chunk('_timescaledb_internal._hyper_2_3_chunk', verbose => TRUE); SELECT ctid, time, val FROM _timescaledb_internal._hyper_2_3_chunk ORDER BY time; SELECT ctid, time, val FROM _timescaledb_internal._hyper_2_4_chunk ORDER BY time; -- There's a special case when a tuple is deleted, but that deletion isn't committed -- But we disallow reorder within transactions for now, if we enable it, we should -- enable the test -- BEGIN; -- DELETE FROM ct2 where time = 2 OR val = 2; -- SELECT reorder_chunk('_timescaledb_internal._hyper_2_4_chunk', verbose => TRUE); -- COMMIT; -- SELECT ctid, time, val FROM _timescaledb_internal._hyper_2_3_chunk ORDER BY time; -- SELECT ctid, time, val FROM _timescaledb_internal._hyper_2_4_chunk ORDER BY time; -- There's a special case when a tuple is inserted earlier in the same txn -- NOTE: this reorder vaccums the one deleted in the previous test -- But we disallow reorder within transactions for now, if we enable it, we should -- enable the test -- BEGIN; -- INSERT INTO ct2 VALUES (12, 2), ( 2, 12); -- SELECT reorder_chunk('_timescaledb_internal._hyper_2_4_chunk', verbose => TRUE); -- COMMIT; -- SELECT ctid, time, val FROM _timescaledb_internal._hyper_2_3_chunk ORDER BY time; -- SELECT ctid, time, val FROM _timescaledb_internal._hyper_2_4_chunk ORDER BY time; -- we do not allow reordering tables with OIDs, note that tables with OIDs are not allowed in PG12 \set ON_ERROR_STOP 0 CREATE table coids (time INTEGER) WITH (OIDS); SELECT create_hypertable('coids', 'time', chunk_time_interval => 5); INSERT INTO coids VALUES (1); SELECT reorder_chunk('_timescaledb_internal._hyper_3_5_chunk', 'coids_time_idx', verbose => TRUE); \set ON_ERROR_STOP 1 SELECT indexrelid::regclass, indisclustered FROM pg_index WHERE indisclustered = true ORDER BY 1; \c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2 \set ON_ERROR_STOP 0 SELECT reorder_chunk('_timescaledb_internal._hyper_2_3_chunk', verbose => TRUE); \set ON_ERROR_STOP 1 -- #3651 incorrect index attribute mapping in reorder_chunk CREATE TABLE i3651(c1 timestamptz NOT NULL,c2 text, c3 text, c4 text); SELECT table_name FROM create_hypertable('i3651','c1'); ALTER TABLE i3651 drop column c2; CREATE UNIQUE INDEX i3651_attmap ON i3651(c1,c4); INSERT INTO i3651 VALUES('2000-01-01','foo','foo'),('2000-01-01','foo','bar'); SELECT reorder_chunk(chunk,'i3651_attmap') from show_chunks('i3651') chunk; -- test error handling when trying to create on internal hypertable CREATE TABLE comp_ht_test(time timestamptz NOT NULL); SELECT table_name FROM create_hypertable('comp_ht_test','time'); ALTER TABLE comp_ht_test SET (timescaledb.compress); SELECT format('%I.%I', ht.schema_name, ht.table_name) AS "INTERNALTABLE" FROM _timescaledb_catalog.hypertable ht INNER JOIN _timescaledb_catalog.hypertable uncompress ON (ht.id = uncompress.compressed_hypertable_id AND uncompress.table_name = 'comp_ht_test') \gset \c :TEST_DBNAME :ROLE_SUPERUSER CREATE INDEX internal_idx ON :INTERNALTABLE(_ts_meta_min_1); \set ON_ERROR_STOP 0 SELECT add_reorder_policy(:'INTERNALTABLE','internal_idx'); \set ON_ERROR_STOP 1