-- Copyright (c) 2016-2018  Timescale, Inc. All Rights Reserved.
--
-- This file is licensed under the Timescale License,
-- see LICENSE-TIMESCALE at the top of the tsl directory.

\c single :ROLE_SUPERUSER
SELECT _timescaledb_internal.stop_background_workers();
SET timescaledb.license_key='CommunityLicense';

CREATE OR REPLACE FUNCTION ts_test_chunk_stats_insert(job_id INTEGER, chunk_id INTEGER, num_times_run INTEGER, last_time_run TIMESTAMPTZ = NULL) RETURNS VOID
AS :TSL_MODULE_PATHNAME LANGUAGE C VOLATILE;

CREATE OR REPLACE FUNCTION delete_job(job_id INTEGER)
RETURNS VOID
AS :TSL_MODULE_PATHNAME, 'ts_test_bgw_job_delete_by_id'
LANGUAGE C VOLATILE STRICT;

\c single :ROLE_DEFAULT_PERM_USER

select * from _timescaledb_config.bgw_policy_drop_chunks;
select * from _timescaledb_config.bgw_policy_recluster;

CREATE TABLE test_table(time timestamptz, junk int);
SELECT create_hypertable('test_table', 'time');
CREATE INDEX second_index on test_table (time);
CREATE INDEX third_index on test_table (time);

select add_recluster_policy('test_table', 'test_table_time_idx') as job_id \gset
-- Noop for duplicate policy
select add_recluster_policy('test_table', 'test_table_time_idx', true);
select add_recluster_policy('test_table', 'second_index', true);
select add_recluster_policy('test_table', 'third_index', true);

\set ON_ERROR_STOP 0
-- Error whenever incorrect arguments are applied (must have table and index)
select add_recluster_policy('test_table', 'bad_index');
select add_recluster_policy('test_table', '');
select add_recluster_policy('test_table');

select add_recluster_policy('test_table', 'second_index');
select add_recluster_policy('test_table', 'third_index');
\set ON_ERROR_STOP 1

select * from _timescaledb_config.bgw_policy_recluster where job_id=:job_id;

-- Now check that default scheduling interval for recluster policy is calculated correctly
-- Should be 1/2 default chunk interval length
CREATE TABLE test_table2(time timestamptz, junk int);
SELECT create_hypertable('test_table2', 'time', chunk_time_interval=>INTERVAL '1 day');
select add_recluster_policy('test_table2', 'test_table2_time_idx');

select * from _timescaledb_config.bgw_job where job_type IN ('drop_chunks', 'recluster');

DROP TABLE test_table2;
-- Make sure that test_table2 recluster policy gets dropped
select * from _timescaledb_config.bgw_job where job_type IN ('drop_chunks', 'recluster');

-- Error whenever incorrect arguments are applied (must have table and interval)
\set ON_ERROR_STOP 0
select add_drop_chunks_policy();
select add_drop_chunks_policy('test_table');
select add_drop_chunks_policy(INTERVAL '3 hours');
select add_drop_chunks_policy('fake_table', INTERVAL '3 month', true);
select add_drop_chunks_policy('fake_table', INTERVAL '3 month');
select add_drop_chunks_policy('test_table', cascade=>true);
\set ON_ERROR_STOP 1

select add_drop_chunks_policy('test_table', INTERVAL '3 month', true);
-- add_*_policy should be noop only for policies with the exact same parameters
select add_drop_chunks_policy('test_table', INTERVAL '3 month', true, true);
-- Should not add new policy with different parameters
select add_drop_chunks_policy('test_table', INTERVAL '3 month', false, true);
select add_drop_chunks_policy('test_table', INTERVAL '1 year', if_not_exists => true);
select add_drop_chunks_policy('test_table', INTERVAL '3 days', if_not_exists => true);
select add_drop_chunks_policy('test_table', INTERVAL '3 days', true, if_not_exists => true);

\set ON_ERROR_STOP 0
select add_drop_chunks_policy('test_table', INTERVAL '3 month', false);
select add_drop_chunks_policy('test_table', INTERVAL '1 year');
select add_drop_chunks_policy('test_table', INTERVAL '3 days');
select add_drop_chunks_policy('test_table', INTERVAL '3 days', true);
\set ON_ERROR_STOP 1

select * from _timescaledb_config.bgw_policy_drop_chunks;
select r.job_id,r.hypertable_id,r.older_than,r.cascade from _timescaledb_config.bgw_policy_drop_chunks as r, _timescaledb_catalog.hypertable as h where r.hypertable_id=h.id and h.table_name='test_table';

select remove_drop_chunks_policy('test_table');

select * from _timescaledb_config.bgw_policy_drop_chunks;
select r.job_id,r.hypertable_id,r.older_than,r.cascade from _timescaledb_config.bgw_policy_drop_chunks as r, _timescaledb_catalog.hypertable as h where r.hypertable_id=h.id and h.table_name='test_table';
select remove_recluster_policy('test_table');

select * from _timescaledb_config.bgw_policy_recluster;
select r.job_id,r.hypertable_id,r.hypertable_index_name from _timescaledb_config.bgw_policy_recluster as r, _timescaledb_catalog.hypertable as h where r.hypertable_id=h.id and h.table_name='test_table';

select add_drop_chunks_policy('test_table', INTERVAL '3 month');
select * from _timescaledb_config.bgw_policy_drop_chunks;
select remove_drop_chunks_policy('test_table');
select * from _timescaledb_config.bgw_policy_drop_chunks;

-- Make sure remove works when there's nothing to remove
select remove_drop_chunks_policy('test_table', true);
select remove_recluster_policy('test_table', true);

\set ON_ERROR_STOP 0
select remove_drop_chunks_policy();
select remove_drop_chunks_policy('fake_table');
select remove_recluster_policy();
select remove_recluster_policy('fake_table');
\set ON_ERROR_STOP 1

\set ON_ERROR_STOP 0
-- This should be noop
select remove_recluster_policy(2, true);
-- Fail with error message
select remove_recluster_policy(2);
\set ON_ERROR_STOP 1

-- Now make sure policy args have correct job deletion dependency
select * from _timescaledb_config.bgw_job where job_type IN ('drop_chunks', 'recluster');

select add_drop_chunks_policy('test_table', INTERVAL '2 month') as job_id \gset
select add_recluster_policy('test_table', 'third_index') as recluster_job_id \gset

select count(*) from _timescaledb_config.bgw_job where id=:job_id;
select count(*) from _timescaledb_config.bgw_job where id=:recluster_job_id;
select count(*) from _timescaledb_config.bgw_policy_drop_chunks where job_id=:job_id;
select count(*) from _timescaledb_config.bgw_policy_recluster where job_id=:recluster_job_id;

select delete_job(:job_id);

select count(*) from _timescaledb_config.bgw_job where id=:job_id;
-- Job args should be gone
select count(*) from _timescaledb_config.bgw_policy_drop_chunks where job_id=:job_id;
-- Job args should still be there
select count(*) from _timescaledb_config.bgw_policy_recluster where job_id=:recluster_job_id;

select delete_job(:recluster_job_id);
select count(*) from _timescaledb_config.bgw_job where id=:recluster_job_id;
-- Job args should be gone
select count(*) from _timescaledb_config.bgw_policy_recluster where job_id=:recluster_job_id;

-- Now make sure policy args have correct job deletion dependency
select add_drop_chunks_policy('test_table', INTERVAL '2 month') as job_id \gset
select add_recluster_policy('test_table', 'third_index') as recluster_job_id \gset

select count(*) from _timescaledb_config.bgw_job where id=:job_id;
select count(*) from _timescaledb_config.bgw_job where id=:recluster_job_id;
select count(*) from _timescaledb_config.bgw_policy_drop_chunks where job_id=:job_id;
select count(*) from _timescaledb_config.bgw_policy_recluster where job_id=:recluster_job_id;
select * from _timescaledb_config.bgw_job;

DROP TABLE test_table;

select count(*) from _timescaledb_config.bgw_job where id=:job_id;
select count(*) from _timescaledb_config.bgw_job where id=:recluster_job_id;
select count(*) from _timescaledb_config.bgw_policy_drop_chunks where job_id=:job_id;
select count(*) from _timescaledb_config.bgw_policy_recluster where job_id=:recluster_job_id;

-- Check that we can't add policies on non-hypertables
CREATE TABLE non_hypertable(junk int, more_junk int);
CREATE INDEX non_ht_index on non_hypertable(junk);

\set ON_ERROR_STOP 0
select add_drop_chunks_policy('non_hypertable', INTERVAL '2 month');
select add_recluster_policy('non_hypertable', 'non_ht_index');
\set ON_ERROR_STOP 1

-- Now make sure things work with multiple policies on multiple hypertables
CREATE TABLE test_table(time timestamptz, junk int);
SELECT create_hypertable('test_table', 'time');
CREATE INDEX second_index on test_table (time);

CREATE TABLE test_table2(time timestamptz, junk int);
SELECT create_hypertable('test_table2', 'time');
CREATE INDEX junk_index on test_table2 (junk);

select add_drop_chunks_policy('test_table', INTERVAL '2 days');
select add_drop_chunks_policy('test_table2', INTERVAL '1 days');

select * from _timescaledb_config.bgw_job where job_type IN ('drop_chunks');
select * from _timescaledb_config.bgw_policy_drop_chunks;

DROP TABLE test_table;

select * from _timescaledb_config.bgw_job where job_type IN ('drop_chunks');
select * from _timescaledb_config.bgw_policy_drop_chunks;

DROP TABLE test_table2;

select * from _timescaledb_config.bgw_job where job_type IN ('drop_chunks');
select * from _timescaledb_config.bgw_policy_drop_chunks;

-- Now test chunk_stat insertion
select ts_test_chunk_stats_insert(123, 123, 45);
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;

\c single :ROLE_SUPERUSER
TRUNCATE _timescaledb_internal.bgw_policy_chunk_stats;
\c single :ROLE_DEFAULT_PERM_USER

-- Now test chunk_stat cascade deletion is correct
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;

CREATE TABLE test_table(time timestamptz, junk int);
SELECT create_hypertable('test_table', 'time');
CREATE INDEX second_index on test_table (time);

insert into test_table values (now(), 1);
insert into test_table values (now() - INTERVAL '5 weeks', 123);

select c.id from _timescaledb_catalog.chunk as c, _timescaledb_catalog.hypertable as h where c.hypertable_id=h.id and h.table_name='test_table';

select c.id as chunk_id from _timescaledb_catalog.chunk as c, _timescaledb_catalog.hypertable as h where c.hypertable_id=h.id and h.table_name='test_table' LIMIT 1 \gset

select add_recluster_policy('test_table', 'second_index') as job_id \gset
-- Simulate recluster job running and setting this stat row
select ts_test_chunk_stats_insert(:job_id, :chunk_id, 1);
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;
select * from _timescaledb_config.bgw_job where job_type='recluster';

-- Deleting a chunk that has nothing to do with the job should do nothing
select c.table_name as other_chunk_name,c.schema_name as other_chunk_schema from _timescaledb_catalog.chunk as c, _timescaledb_catalog.hypertable as h where c.id != :chunk_id \gset
select concat(:'other_chunk_schema','.',:'other_chunk_name') as other_chunk \gset

DROP TABLE :other_chunk;

select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;
select * from _timescaledb_config.bgw_job where job_type='recluster';

-- Dropping the hypertable should drop the chunk, which should drop the recluster policy
DROP TABLE test_table;
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;
select * from _timescaledb_config.bgw_job where job_type='recluster';

-- Now check dropping a job will drop the chunk_stat row
CREATE TABLE test_table(time timestamptz, junk int);
SELECT create_hypertable('test_table', 'time');

select add_recluster_policy('test_table', 'test_table_time_idx') as job_id \gset
select add_drop_chunks_policy('test_table', INTERVAL '2 days', true);

select ts_test_chunk_stats_insert(:job_id, 123, 1);
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;
select * from _timescaledb_config.bgw_job where job_type in ('drop_chunks', 'recluster');

-- Dropping the drop_chunks job should not affect the chunk_stats row
select remove_drop_chunks_policy('test_table');
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;
select * from _timescaledb_config.bgw_job where job_type in ('drop_chunks', 'recluster');

select remove_recluster_policy('test_table');
-- Row should be gone
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;
select * from _timescaledb_config.bgw_job where job_type in ('drop_chunks', 'recluster');

-- Now test if alter_job_schedule works
select add_recluster_policy('test_table', 'test_table_time_idx') as job_id \gset
 select * from _timescaledb_config.bgw_job where id=:job_id;
-- No change
select * from alter_policy_schedule(:job_id);
-- Changes expected
select * from alter_policy_schedule(:job_id, INTERVAL '3 years', INTERVAL '5 min', 5, INTERVAL '123 sec');
select * from alter_policy_schedule(:job_id, INTERVAL '123 years');
select * from alter_policy_schedule(:job_id, retry_period => INTERVAL '33 hours');
select * from alter_policy_schedule(:job_id, max_runtime => INTERVAL '456 sec');
select * from alter_policy_schedule(:job_id, max_retries => 0);
select * from alter_policy_schedule(:job_id, max_retries => -1);
select * from alter_policy_schedule(:job_id, max_retries => 20);

-- No change
select * from alter_policy_schedule(:job_id, max_runtime => NULL);
select * from alter_policy_schedule(:job_id, max_retries => NULL);

-- Check if_exists boolean works correctly
select * from alter_policy_schedule(1234, if_exists => TRUE);

\set ON_ERROR_STOP 0
select * from alter_policy_schedule(1234);
\set ON_ERROR_STOP 1

select remove_recluster_policy('test_table');

\c single :ROLE_SUPERUSER
set session timescaledb.license_key='Community';

-- Now make sure everything fails in the Community (non-enterprise) edition
\set ON_ERROR_STOP 0 
select add_recluster_policy('test_table', 'test_table_time_idx');
select add_drop_chunks_policy('test_table', INTERVAL '4 months', true);
select remove_recluster_policy('test_table');
select remove_drop_chunks_policy('test_table');
select alter_policy_schedule(12345);
\set ON_ERROR_STOP 1
