-- 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.

\c :TEST_DBNAME :ROLE_SUPERUSER
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;

\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER

SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

CREATE TABLE test_table(time timestamptz, junk int);
CREATE TABLE test_table_int(time bigint, junk int);

SELECT create_hypertable('test_table', 'time');
SELECT create_hypertable('test_table_int', 'time', chunk_time_interval => 1);

CREATE INDEX second_index on test_table (time);
CREATE INDEX third_index on test_table (time);

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

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

select add_reorder_policy('test_table', 'second_index');
select add_reorder_policy('test_table', 'third_index');
select add_reorder_policy(NULL, 'third_index');
select add_reorder_policy(2, 'third_index');
\set ON_ERROR_STOP 1

select * from _timescaledb_catalog.bgw_job where id=:job_id;

-- Now check that default scheduling interval for reorder 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_reorder_policy('test_table2', 'test_table2_time_idx');

SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

DROP TABLE test_table2;
-- Make sure that test_table2 reorder policy gets dropped
SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

-- Error whenever incorrect arguments are applied (must have table and interval)
\set ON_ERROR_STOP 0
select add_retention_policy();
select add_retention_policy('test_table');
select add_retention_policy(INTERVAL '3 hours');
select add_retention_policy('test_table', INTERVAL 'haha');
select add_retention_policy('test_table', 'haha');
select add_retention_policy('test_table', 42);
select add_retention_policy('fake_table', INTERVAL '3 month');
\set ON_ERROR_STOP 1

select add_retention_policy('test_table', INTERVAL '3 month', true);
-- Should not add new policy with different parameters
select add_retention_policy('test_table', INTERVAL '3 month', true);
select add_retention_policy('test_table', INTERVAL '1 year', if_not_exists => true);
select add_retention_policy('test_table', INTERVAL '3 days', if_not_exists => true);
select add_retention_policy('test_table', INTERVAL '3 days', if_not_exists => true);

SELECT * FROM _timescaledb_catalog.bgw_job WHERE proc_name = 'policy_retention' ORDER BY id;

\set ON_ERROR_STOP 0
select add_retention_policy('test_table', INTERVAL '1 year');
select add_retention_policy('test_table', INTERVAL '3 days');
\set ON_ERROR_STOP 1

SELECT * FROM _timescaledb_catalog.bgw_job WHERE proc_name = 'policy_retention' ORDER BY id;

select remove_retention_policy('test_table');

-- Test set_integer_now_func and add_retention_policy with
-- hypertables that have integer time dimension

select * from _timescaledb_catalog.dimension;
\c :TEST_DBNAME :ROLE_SUPERUSER

CREATE SCHEMA IF NOT EXISTS my_new_schema;
create or replace function my_new_schema.dummy_now2() returns BIGINT LANGUAGE SQL IMMUTABLE as  'SELECT 1::BIGINT';
grant usage on SCHEMA my_new_schema to public;
grant execute on ALL FUNCTIONS IN SCHEMA my_new_schema to public;
select set_integer_now_func('test_table_int', 'my_new_schema.dummy_now2');

\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
select * from _timescaledb_catalog.dimension;

SELECT * FROM _timescaledb_catalog.bgw_job WHERE proc_name = 'policy_retention' ORDER BY id;
select remove_reorder_policy('test_table');

SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

select add_retention_policy('test_table', INTERVAL '3 month');
SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;
select remove_retention_policy('test_table');
SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

select add_retention_policy('test_table_int', 1);
SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;
-- Should not add new policy with different parameters
select add_retention_policy('test_table_int', 2, true);

select remove_retention_policy('test_table_int');
SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

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

\set ON_ERROR_STOP 0
select remove_retention_policy();
select remove_retention_policy('fake_table');
select remove_reorder_policy();
select remove_reorder_policy('fake_table');
\set ON_ERROR_STOP 1

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

-- Now make sure policy args have correct job deletion dependency
SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

select add_retention_policy('test_table', INTERVAL '2 month') as job_id \gset
select add_reorder_policy('test_table', 'third_index') as reorder_job_id \gset

select count(*) from _timescaledb_catalog.bgw_job where id=:job_id;
select count(*) from _timescaledb_catalog.bgw_job where id=:reorder_job_id;

select delete_job(:job_id);

select count(*) from _timescaledb_catalog.bgw_job where id=:job_id;
-- Job config should still be there
select count(*) from _timescaledb_catalog.bgw_job where id=:reorder_job_id;

select delete_job(:reorder_job_id);
select count(*) from _timescaledb_catalog.bgw_job where id=:reorder_job_id;

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

select count(*) from _timescaledb_catalog.bgw_job where id=:job_id;
select count(*) from _timescaledb_catalog.bgw_job where id=:reorder_job_id;

DROP TABLE test_table;

select count(*) from _timescaledb_catalog.bgw_job where id=:job_id;
select count(*) from _timescaledb_catalog.bgw_job where id=:reorder_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_retention_policy('non_hypertable', INTERVAL '2 month');
select add_reorder_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_retention_policy('test_table', INTERVAL '2 days');
select add_retention_policy('test_table2', INTERVAL '1 days');

SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

DROP TABLE test_table;
DROP TABLE test_table_int;

SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

DROP TABLE test_table2;

SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

-- 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 :TEST_DBNAME :ROLE_SUPERUSER
TRUNCATE _timescaledb_internal.bgw_policy_chunk_stats;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER

-- Now test chunk_stat 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' ORDER BY c.id;

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' ORDER BY c.id LIMIT 1 \gset

select add_reorder_policy('test_table', 'second_index') as job_id \gset
-- Simulate reorder 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_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

-- 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_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

-- Dropping the hypertable should drop the chunk, which should drop the reorder policy
DROP TABLE test_table;
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;
SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

-- 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_reorder_policy('test_table', 'test_table_time_idx') as job_id \gset
select add_retention_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_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

-- Dropping the drop_chunks job should not affect the chunk_stats row
select remove_retention_policy('test_table');
select job_id,chunk_id,num_times_job_run from _timescaledb_internal.bgw_policy_chunk_stats;
SELECT * FROM _timescaledb_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

select remove_reorder_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_catalog.bgw_job WHERE id >= 1000 ORDER BY id;

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

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

--change schedule_interval when bgw_job_stat does not exist
select * from alter_job(:job_id, schedule_interval=>'1 min');
select count(*) = 0 from _timescaledb_internal.bgw_job_stat where job_id = :job_id;
--set next_start when bgw_job_stat does not exist
select * from alter_job(:job_id, next_start=>'2001-01-01 01:01:01');
--change schedule_interval when no last_finish set
select * from alter_job(:job_id, schedule_interval=>'10 min');
--next_start overrides any schedule_interval changes
select * from alter_job(:job_id, schedule_interval=>'20 min', next_start=>'2002-01-01 01:01:01');

--set the last_finish manually
\c :TEST_DBNAME :ROLE_SUPERUSER
UPDATE _timescaledb_internal.bgw_job_stat SET last_finish = '2003-01-01:01:01:01' WHERE job_id = :job_id;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER

--not changing the interval doesn't change the next_start
select * from alter_job(:job_id, schedule_interval=>'20 min');
--changing the interval changes next_start
select * from alter_job(:job_id, schedule_interval=>'30 min');
--explicit next start overrides.
select * from alter_job(:job_id, schedule_interval=>'40 min', next_start=>'2004-01-01 01:01:01');
--test pausing
select * from alter_job(:job_id, next_start=>'infinity');
--test that you can use now() to unpause
select next_start = now() from alter_job(:job_id, next_start=>now());
--test pausing/resuming via scheduled parameter
select job_id from alter_job(:job_id, scheduled=>false);
select job_status, next_start from timescaledb_information.job_stats where job_id = :job_id;
select job_id from alter_job(:job_id, scheduled=>true);
select job_status from timescaledb_information.job_stats where job_id = :job_id;

\set ON_ERROR_STOP 0
-- negative infinity disallowed (used as special value)
select * from alter_job(:job_id, next_start=>'-infinity');
-- index should exist
select * from alter_job(:job_id,
       config => '{"index_name": "non-existent", "hypertable_id": 7}');
-- index should be an index on the hypertable
select * from alter_job(:job_id,
       config => '{"index_name": "non_ht_index", "hypertable_id": 7}');
-- hypertable should exist
select * from alter_job(:job_id,
       config => '{"index_name": "test_table_time_idx", "hypertable_id": 47}');
\set ON_ERROR_STOP 1

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

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

select remove_reorder_policy('test_table');

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

-- Test for failure cases
\set ON_ERROR_STOP 0
select alter_job(12345);
\set ON_ERROR_STOP 1

select add_reorder_policy('test_table', 'test_table_time_idx') as reorder_job_id \gset
select add_retention_policy('test_table', INTERVAL '4 months', true) as drop_chunks_job_id \gset

\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
\set ON_ERROR_STOP 0
select from alter_job(:reorder_job_id, max_runtime => NULL);
select from alter_job(:drop_chunks_job_id, max_runtime => NULL);
\set ON_ERROR_STOP 1
