-- 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/rand_generator.sql
\c :TEST_DBNAME :ROLE_SUPERUSER
\ir include/compression_utils.sql
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER

CREATE TABLE test1 ("Time" timestamptz, i integer, value integer);
SELECT table_name from create_hypertable('test1', 'Time', chunk_time_interval=> INTERVAL '1 hour');

-- This will generate 24 chunks
INSERT INTO test1
SELECT t, i, gen_rand_minstd()
FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-03 0:59', '1 minute') t
CROSS JOIN generate_series(1, 5, 1) i;

-- Compression is set to merge those 24 chunks into 12 2 hour chunks
ALTER TABLE test1 set (timescaledb.compress, timescaledb.compress_segmentby='i', timescaledb.compress_orderby='"Time"', timescaledb.compress_chunk_time_interval='2 hours');

SELECT
  $$
  SELECT * FROM test1 ORDER BY i, "Time"
  $$ AS "QUERY" \gset

SELECT 'test1' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_merge.sql
\set TYPE timestamptz
\set ORDER_BY_COL_NAME Time
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql

DROP TABLE test1;

CREATE TABLE test2 ("Time" timestamptz, i integer, loc integer, value integer);
SELECT table_name from create_hypertable('test2', 'Time', chunk_time_interval=> INTERVAL '1 hour');

-- This will generate 24 1 hour chunks.
INSERT INTO test2
SELECT t, i, gen_rand_minstd()
FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-03 0:59', '1 minute') t
CROSS JOIN generate_series(1, 5, 1) i;

-- Setting compression with time DESC to confirm we are don't have to recompress due to DESC order
-- This is made possible by removal of sequence numbers
\set VERBOSITY default
ALTER TABLE test2 set (timescaledb.compress, timescaledb.compress_segmentby='i', timescaledb.compress_orderby='"Time" DESC', timescaledb.compress_chunk_time_interval='10 hours');
\set VERBOSITY terse

-- Verify we are not generating unordered chunks
BEGIN;
  SELECT count(compress_chunk(chunk,  true)) FROM show_chunks('test2') chunk;
  SELECT format('%I.%I',ch.schema_name,ch.table_name) AS "CHUNK"
    FROM _timescaledb_catalog.chunk ch
    JOIN _timescaledb_catalog.hypertable ht ON ht.id=ch.hypertable_id
    JOIN _timescaledb_catalog.hypertable ht2 ON ht.id=ht2.compressed_hypertable_id AND ht2.table_name='test2' LIMIT 1 \gset

  -- We want to sure we are not fully recompressing them which will make
  -- the chunk contain multiple batches per segment group
  SELECT count(*)
  FROM :CHUNK
  WHERE i = 1;
ROLLBACK;

-- Compression is set to merge those 24 chunks into 3 chunks, two 10 hour chunks and a single 4 hour chunk.
\set VERBOSITY default
ALTER TABLE test2 set (timescaledb.compress, timescaledb.compress_segmentby='i', timescaledb.compress_orderby='loc,"Time"', timescaledb.compress_chunk_time_interval='10 hours');
\set VERBOSITY terse

-- Verify we are fully recompressing unordered chunks
BEGIN;
  SELECT count(compress_chunk(chunk,  true)) FROM show_chunks('test2') chunk;
  SELECT format('%I.%I',ch.schema_name,ch.table_name) AS "CHUNK"
    FROM _timescaledb_catalog.chunk ch
    JOIN _timescaledb_catalog.hypertable ht ON ht.id=ch.hypertable_id
    JOIN _timescaledb_catalog.hypertable ht2 ON ht.id=ht2.compressed_hypertable_id AND ht2.table_name='test2' LIMIT 1 \gset

  -- Not using time as first column in orderby makes the merged chunks unordered
  -- We want to sure we are fully recompressing them which will make only
  -- a single batch per segment group
  SELECT count(*)
  FROM :CHUNK
  WHERE i = 1;
ROLLBACK;

SELECT
  $$
  SELECT * FROM test2 ORDER BY i, "Time"
  $$ AS "QUERY" \gset

SELECT 'test2' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_merge.sql
\set TYPE integer
\set ORDER_BY_COL_NAME loc
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql

DROP TABLE test2;

CREATE TABLE test3 ("Time" timestamptz, i integer, loc integer, value integer);
SELECT table_name from create_hypertable('test3', 'Time', chunk_time_interval=> INTERVAL '1 hour');
SELECT table_name from  add_dimension('test3', 'i', number_partitions=> 3);

-- This will generate 25 1 hour chunks with a closed space dimension.
INSERT INTO test3 SELECT t, 1, gen_rand_minstd(), gen_rand_minstd() FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-02 12:59', '1 minute') t;
INSERT INTO test3 SELECT t, 2, gen_rand_minstd(), gen_rand_minstd() FROM generate_series('2018-03-02 13:00'::TIMESTAMPTZ, '2018-03-03 0:59', '1 minute') t;
INSERT INTO test3 SELECT t, 3, gen_rand_minstd(), gen_rand_minstd() FROM generate_series('2018-03-02 2:00'::TIMESTAMPTZ, '2018-03-02 2:01', '1 minute') t;

-- Compression is set to merge those 25 chunks into 12 2 hour chunks and a single 1 hour chunks on a different space dimensions.
\set VERBOSITY default
ALTER TABLE test3 set (timescaledb.compress, timescaledb.compress_orderby='loc,"Time"', timescaledb.compress_chunk_time_interval='2 hours');
\set VERBOSITY terse

SELECT
  $$
  SELECT * FROM test3 WHERE i = 1 ORDER BY "Time"
  $$ AS "QUERY" \gset

SELECT 'test3' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_merge.sql

\set TYPE integer
\set ORDER_BY_COL_NAME loc
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql

DROP TABLE test3;

CREATE TABLE test4 ("Time" timestamptz, i integer, loc integer, value integer);
SELECT table_name from create_hypertable('test4', 'Time', chunk_time_interval=> INTERVAL '1 hour');
-- Setting compress_chunk_time_interval to non-multiple of chunk_time_interval should emit a warning.
\set VERBOSITY default
ALTER TABLE test4 set (timescaledb.compress, timescaledb.compress_orderby='loc,"Time"', timescaledb.compress_chunk_time_interval='90 minutes');
\set VERBOSITY terse

DROP TABLE test4;

CREATE TABLE test5 ("Time" timestamptz, i integer, value integer);
SELECT table_name from create_hypertable('test5', 'Time', chunk_time_interval=> INTERVAL '1 hour');

-- This will generate 24 chunks. Note that we are forcing everyting into a single segment so we use table scan to when merging chunks.
INSERT INTO test5 SELECT t, 1, gen_rand_minstd() FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-03 0:59', '1 minute') t;

-- Compression is set to merge those 24 chunks into 1 24 hour chunk
ALTER TABLE test5 set (timescaledb.compress, timescaledb.compress_segmentby='i', timescaledb.compress_orderby='"Time"', timescaledb.compress_chunk_time_interval='24 hours');

SELECT
  $$
  SELECT * FROM test5 ORDER BY i, "Time"
  $$ AS "QUERY" \gset

SELECT compress_chunk(i) FROM show_chunks('test5') i LIMIT 4;

SELECT format('%I.%I',ch.schema_name,ch.table_name) AS "CHUNK"
  FROM _timescaledb_catalog.chunk ch
  JOIN _timescaledb_catalog.hypertable ht ON ht.id=ch.hypertable_id
  JOIN _timescaledb_catalog.hypertable ht2 ON ht.id=ht2.compressed_hypertable_id AND ht2.table_name='test5' \gset

SELECT schemaname || '.' || indexname AS "INDEXNAME"
FROM pg_indexes i
INNER JOIN _timescaledb_catalog.chunk cc ON i.schemaname = cc.schema_name and i.tablename = cc.table_name
INNER JOIN _timescaledb_catalog.chunk c ON (cc.id = c.compressed_chunk_id)
LIMIT 1 \gset


DROP INDEX :INDEXNAME;

-- Merging works without indexes on compressed chunks
SELECT compress_chunk(i, true) FROM show_chunks('test5') i LIMIT 5;

SELECT 'test5' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_merge.sql

DROP TABLE test5;

CREATE TABLE test6 ("Time" timestamptz, i integer, value integer);
SELECT table_name from create_hypertable('test6', 'Time', chunk_time_interval=> INTERVAL '1 hour');

-- This will generate 24 chunks
INSERT INTO test6
SELECT t, i, gen_rand_minstd()
FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-03 0:59', '1 minute') t
CROSS JOIN generate_series(1, 5, 1) i;

-- Compression is set to merge those 24 chunks into 12 2 hour chunks.
ALTER TABLE test6 set (timescaledb.compress, timescaledb.compress_segmentby='i', timescaledb.compress_orderby='"Time"', timescaledb.compress_chunk_time_interval='2 hours');

SELECT compress_chunk(i) FROM show_chunks('test6') i;
SELECT count(*) as number_of_chunks FROM show_chunks('test6');

-- This will generate another 24 chunks
INSERT INTO test6
SELECT t, i, gen_rand_minstd()
FROM generate_series('2018-03-03 1:00'::TIMESTAMPTZ, '2018-03-04 0:59', '1 minute') t
CROSS JOIN generate_series(1, 5, 1) i;
-- Altering compress chunk time interval will cause us to create 6 chunks from the additional 24 chunks.
ALTER TABLE test6 set (timescaledb.compress_chunk_time_interval='4 hours');

SELECT compress_chunk(i, true) FROM show_chunks('test6') i;
SELECT count(*) as number_of_chunks FROM show_chunks('test6');

-- This will generate another 3 chunks
INSERT INTO test6
SELECT t, i, gen_rand_minstd()
FROM generate_series('2018-03-04 1:00'::TIMESTAMPTZ, '2018-03-04 3:59', '1 minute') t
CROSS JOIN generate_series(1, 5, 1) i;
-- Altering compress chunk time interval will cause us to create 3 chunks from the additional 3 chunks.
-- Setting compressed chunk to anything less than chunk interval should disable merging chunks.
ALTER TABLE test6 set (timescaledb.compress_chunk_time_interval='30 minutes');

SELECT compress_chunk(i, true) FROM show_chunks('test6') i;
SELECT count(*) as number_of_chunks FROM show_chunks('test6');

-- This will generate another 3 chunks
INSERT INTO test6
SELECT t, i, gen_rand_minstd()
FROM generate_series('2018-03-04 4:00'::TIMESTAMPTZ, '2018-03-04 6:59', '1 minute') t
CROSS JOIN generate_series(1, 5, 1) i;
-- Altering compress chunk time interval will cause us to create 3 chunks from the additional 3 chunks.
-- Setting compressed chunk to anything less than chunk interval should disable merging chunks.
ALTER TABLE test6 set (timescaledb.compress_chunk_time_interval=0);

SELECT compress_chunk(i, true) FROM show_chunks('test6') i;
SELECT count(*) as number_of_chunks FROM show_chunks('test6');

-- Setting compress chunk time to NULL will error out.
\set ON_ERROR_STOP 0
ALTER TABLE test6 set (timescaledb.compress_chunk_time_interval=NULL);
\set ON_ERROR_STOP 1

DROP TABLE test6;

CREATE TABLE test7 ("Time" timestamptz, i integer, j integer, k integer, value integer);
SELECT table_name from create_hypertable('test7', 'Time', chunk_time_interval=> INTERVAL '1 hour');

-- This will generate 24 chunks
INSERT INTO test7
SELECT t, i, gen_rand_minstd(), gen_rand_minstd(), gen_rand_minstd()
FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-03 0:59', '1 minute') t
CROSS JOIN generate_series(1, 5, 1) i;

-- Compression is set to merge those 24 chunks into 12 2 hour chunks with ordering by j column before time column, causing recompression to occur during merge.
ALTER TABLE test7 set (timescaledb.compress, timescaledb.compress_segmentby='i', timescaledb.compress_orderby='j, "Time" desc', timescaledb.compress_chunk_time_interval='2 hours');

SELECT
  $$
  SELECT * FROM test7 ORDER BY i, "Time"
  $$ AS "QUERY" \gset

SELECT 'test7' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_merge.sql

DROP TABLE test7;

--#5090
CREATE TABLE test8(time TIMESTAMPTZ NOT NULL, value DOUBLE PRECISION NOT NULL, series_id BIGINT NOT NULL);

SELECT create_hypertable('test8', 'time', chunk_time_interval => INTERVAL '1 h');

ALTER TABLE test8 set (timescaledb.compress,
    timescaledb.compress_segmentby = 'series_id',
    timescaledb.compress_orderby = 'time',
    timescaledb.compress_chunk_time_interval = '1 day');

INSERT INTO test8 (time, series_id, value) SELECT t, s, 1 FROM generate_series(NOW(), NOW()+INTERVAL'4h', INTERVAL '30s') t CROSS JOIN generate_series(0, 100, 1) s;

SELECT compress_chunk(c, true) FROM show_chunks('test8') c LIMIT 4;
SET enable_indexscan TO OFF;
SET enable_seqscan TO OFF;
SET enable_bitmapscan TO ON;

SELECT count(*) FROM test8 WHERE series_id = 1;

-- Verify rollup is prevented when compression settings differ
CREATE TABLE test9(time TIMESTAMPTZ NOT NULL, value DOUBLE PRECISION NOT NULL, series_id BIGINT NOT NULL);
SELECT create_hypertable('test9', 'time', chunk_time_interval => INTERVAL '1 h');

ALTER TABLE test9 set (timescaledb.compress,
    timescaledb.compress_segmentby = 'series_id',
    timescaledb.compress_orderby = 'time',
    timescaledb.compress_chunk_time_interval = '1 day');

-- create chunk and compress
INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 00:00:00'::TIMESTAMPTZ, 1, 1;
SELECT compress_chunk(show_chunks('test9'), true);
INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 01:00:00'::TIMESTAMPTZ, 1, 1;

-- should be 2 chunk before rollup
SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2;
SELECT compress_chunk(show_chunks('test9'), true);
-- should be 1 chunk because of rollup
SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2;

INSERT INTO test9 (time, series_id, value) SELECT '2020-01-01 02:00:00'::TIMESTAMPTZ, 1, 1;
-- should be 2 chunks again
SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2;

ALTER TABLE test9 SET (timescaledb.compress_segmentby = '');
BEGIN;
  SELECT compress_chunk(show_chunks('test9'), true);
  -- should not be rolled up
  SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2;
ROLLBACK;

ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time DESC');
BEGIN;
  SELECT compress_chunk(show_chunks('test9'), true);
  -- should not be rolled up
  SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2;
ROLLBACK;

ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time NULLS FIRST');
BEGIN;
  SELECT compress_chunk(show_chunks('test9'), true);
  -- should not be rolled up
  SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2;
ROLLBACK;

-- reset back to original settings
ALTER TABLE test9 SET (timescaledb.compress_segmentby = 'series_id', timescaledb.compress_orderby = 'time');
BEGIN;
  SELECT compress_chunk(show_chunks('test9'), true);
  -- should be rolled up
  SELECT hypertable_name, range_start, range_end FROM timescaledb_information.chunks WHERE hypertable_name = 'test9' ORDER BY 2;
ROLLBACK;

-- Test RowExclusiveLock on compressed chunk during chunk rollup using a GUC
CREATE TABLE test10 ("Time" timestamptz, i integer, value integer);
SELECT table_name from create_hypertable('test10', 'Time', chunk_time_interval=> INTERVAL '1 hour');

INSERT INTO test10
SELECT t, i, gen_rand_minstd()
FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-02 3:59', '10 minutes') t
CROSS JOIN generate_series(1, 3, 1) i;

ALTER TABLE test10 set (timescaledb.compress, timescaledb.compress_segmentby='i', timescaledb.compress_orderby='"Time"', timescaledb.compress_chunk_time_interval='2 hours');
SHOW timescaledb.enable_rowlevel_compression_locking;
SET timescaledb.enable_rowlevel_compression_locking to on;

SELECT compress_chunk(show_chunks('test10'));

RESET timescaledb.enable_rowlevel_compression_locking;
DROP TABLE test10;

RESET enable_indexscan;
RESET enable_seqscan;
RESET enable_bitmapscan;

-- Test chunk merging with default compression settings
CREATE TABLE test_defaults ("Time" timestamptz, i integer, value integer) WITH (tsdb.hypertable, tsdb.chunk_interval='1 hour');
CREATE INDEX ON test_defaults (i);
ALTER TABLE test_defaults set (timescaledb.compress, timescaledb.compress_chunk_time_interval='2 hours');
SELECT delete_job(1000);

-- This will generate 24 chunks
INSERT INTO test_defaults
SELECT t, i, gen_rand_minstd()
FROM generate_series('2018-03-02 1:00'::TIMESTAMPTZ, '2018-03-03 0:59', '1 minute') t
CROSS JOIN generate_series(1, 5, 1) i;

-- Compression is set to merge those 24 chunks into 12 2 hour chunks

SELECT
  $$
  SELECT * FROM test_defaults ORDER BY i, "Time"
  $$ AS "QUERY" \gset

SELECT 'test_defaults' AS "HYPERTABLE_NAME" \gset
\ir include/compression_test_merge.sql
\set TYPE timestamptz
\set ORDER_BY_COL_NAME Time
\set SEGMENT_META_COL_MIN _ts_meta_min_1
\set SEGMENT_META_COL_MAX _ts_meta_max_1
\ir include/compression_test_hypertable_segment_meta.sql

DROP TABLE test_defaults;
