-- 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.
-- test add and remove refresh policy apis
SET ROLE :ROLE_DEFAULT_PERM_USER;
--TEST1 ---
--basic test with count
CREATE TABLE int_tab (a integer, b integer, c integer);
SELECT table_name FROM create_hypertable('int_tab', 'a', chunk_time_interval=> 10);
 table_name 
------------
 int_tab

INSERT INTO int_tab VALUES( 3 , 16 , 20);
INSERT INTO int_tab VALUES( 1 , 10 , 20);
INSERT INTO int_tab VALUES( 1 , 11 , 20);
INSERT INTO int_tab VALUES( 1 , 12 , 20);
INSERT INTO int_tab VALUES( 1 , 13 , 20);
INSERT INTO int_tab VALUES( 1 , 14 , 20);
INSERT INTO int_tab VALUES( 2 , 14 , 20);
INSERT INTO int_tab VALUES( 2 , 15 , 20);
INSERT INTO int_tab VALUES( 2 , 16 , 20);
CREATE OR REPLACE FUNCTION integer_now_int_tab() returns int LANGUAGE SQL STABLE as $$ SELECT coalesce(max(a), 0) FROM int_tab $$;
SELECT set_integer_now_func('int_tab', 'integer_now_int_tab');
 set_integer_now_func 
----------------------
 

CREATE MATERIALIZED VIEW mat_m1( a, countb )
WITH (timescaledb.continuous, timescaledb.materialized_only=true)
as
SELECT a, count(b)
FROM int_tab
GROUP BY time_bucket(1, a), a WITH NO DATA;
\c :TEST_DBNAME :ROLE_SUPERUSER
SET timezone TO PST8PDT;
DELETE FROM _timescaledb_catalog.bgw_job WHERE TRUE;
SET ROLE :ROLE_DEFAULT_PERM_USER;
SELECT count(*) FROM _timescaledb_catalog.bgw_job;
 count 
-------
     0

\set ON_ERROR_STOP 0
\set VERBOSITY default
-- Test 1 step policy for integer type buckets
ALTER materialized view mat_m1 set (timescaledb.compress = true);
NOTICE:  defaulting compress_orderby to time_partition_col,a
-- No policy is added if one errors out
SELECT timescaledb_experimental.add_policies('mat_m1', refresh_start_offset => 1, refresh_end_offset => 10, compress_after => 11, drop_after => 20);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "integer".
SELECT timescaledb_experimental.show_policies('mat_m1');
 show_policies 
---------------

-- All policies are added in one step
SELECT timescaledb_experimental.add_policies('mat_m1', refresh_start_offset => 10, refresh_end_offset => 1, compress_after => 11, drop_after => 20);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('mat_m1');
                                                                show_policies                                                                
---------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": 11, "compress_interval": "@ 1 day"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": 1, "refresh_start_offset": 10}
 {"drop_after": 20, "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

--Test coverage: new view for policies on CAggs
SELECT * FROM timescaledb_experimental.policies ORDER BY relation_name, proc_name;
 relation_name | relation_schema | schedule_interval |      proc_schema       |              proc_name              |                            config                             |   hypertable_schema   |      hypertable_name       
---------------+-----------------+-------------------+------------------------+-------------------------------------+---------------------------------------------------------------+-----------------------+----------------------------
 mat_m1        | public          | @ 1 day           | _timescaledb_functions | policy_compression                  | {"hypertable_id": 2, "compress_after": 11}                    | _timescaledb_internal | _materialized_hypertable_2
 mat_m1        | public          | @ 1 hour          | _timescaledb_functions | policy_refresh_continuous_aggregate | {"end_offset": 1, "start_offset": 10, "mat_hypertable_id": 2} | _timescaledb_internal | _materialized_hypertable_2
 mat_m1        | public          | @ 1 day           | _timescaledb_functions | policy_retention                    | {"drop_after": 20, "hypertable_id": 2}                        | _timescaledb_internal | _materialized_hypertable_2

--Test coverage: new view for policies only shows the policies for  CAggs
SELECT add_retention_policy('int_tab', 20);
 add_retention_policy 
----------------------
                 1003

SELECT * FROM timescaledb_experimental.policies ORDER BY relation_name, proc_name;
 relation_name | relation_schema | schedule_interval |      proc_schema       |              proc_name              |                            config                             |   hypertable_schema   |      hypertable_name       
---------------+-----------------+-------------------+------------------------+-------------------------------------+---------------------------------------------------------------+-----------------------+----------------------------
 mat_m1        | public          | @ 1 day           | _timescaledb_functions | policy_compression                  | {"hypertable_id": 2, "compress_after": 11}                    | _timescaledb_internal | _materialized_hypertable_2
 mat_m1        | public          | @ 1 hour          | _timescaledb_functions | policy_refresh_continuous_aggregate | {"end_offset": 1, "start_offset": 10, "mat_hypertable_id": 2} | _timescaledb_internal | _materialized_hypertable_2
 mat_m1        | public          | @ 1 day           | _timescaledb_functions | policy_retention                    | {"drop_after": 20, "hypertable_id": 2}                        | _timescaledb_internal | _materialized_hypertable_2

SELECT remove_retention_policy('int_tab');
 remove_retention_policy 
-------------------------
 

-- Test for duplicated policies (issue #5492)
CREATE MATERIALIZED VIEW mat_m2( a, sumb )
WITH (timescaledb.continuous, timescaledb.materialized_only=true)
as
SELECT a, sum(b)
FROM int_tab
GROUP BY time_bucket(1, a), a WITH NO DATA;
-- add refresh policy
SELECT timescaledb_experimental.add_policies('mat_m2', refresh_start_offset => 10, refresh_end_offset => 1);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('mat_m2');
                                                                show_policies                                                                
---------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": 1, "refresh_start_offset": 10}

-- check for only one refresh policy for each cagg
SELECT * FROM timescaledb_experimental.policies WHERE proc_name ~ 'refresh' ORDER BY relation_name, proc_name;
 relation_name | relation_schema | schedule_interval |      proc_schema       |              proc_name              |                            config                             |   hypertable_schema   |      hypertable_name       
---------------+-----------------+-------------------+------------------------+-------------------------------------+---------------------------------------------------------------+-----------------------+----------------------------
 mat_m1        | public          | @ 1 hour          | _timescaledb_functions | policy_refresh_continuous_aggregate | {"end_offset": 1, "start_offset": 10, "mat_hypertable_id": 2} | _timescaledb_internal | _materialized_hypertable_2
 mat_m2        | public          | @ 1 hour          | _timescaledb_functions | policy_refresh_continuous_aggregate | {"end_offset": 1, "start_offset": 10, "mat_hypertable_id": 4} | _timescaledb_internal | _materialized_hypertable_4

SELECT timescaledb_experimental.remove_all_policies('mat_m2');
 remove_all_policies 
---------------------
 t

DROP MATERIALIZED VIEW mat_m2;
-- Alter policies
SELECT timescaledb_experimental.alter_policies('mat_m1',  refresh_start_offset => 11, compress_after=>13, drop_after => 25);
 alter_policies 
----------------
 t

SELECT timescaledb_experimental.show_policies('mat_m1');
                                                                show_policies                                                                
---------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": 13, "compress_interval": "@ 1 day"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": 1, "refresh_start_offset": 11}
 {"drop_after": 25, "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Remove one or more policy
SELECT timescaledb_experimental.remove_policies('mat_m1', false, 'policy_refresh_continuous_aggregate', 'policy_compression');
 remove_policies 
-----------------
 t

SELECT timescaledb_experimental.show_policies('mat_m1');
                                     show_policies                                      
----------------------------------------------------------------------------------------
 {"drop_after": 25, "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Add one policy
SELECT timescaledb_experimental.add_policies('mat_m1', refresh_start_offset => 10, refresh_end_offset => 1);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('mat_m1');
                                                                show_policies                                                                
---------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": 1, "refresh_start_offset": 10}
 {"drop_after": 25, "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Remove all policies
SELECT timescaledb_experimental.remove_policies('mat_m1', false, 'policy_refresh_continuous_aggregate', 'policy_retention');
 remove_policies 
-----------------
 t

SELECT timescaledb_experimental.show_policies('mat_m1');
 show_policies 
---------------

--Cross policy checks
--refresh and compression policy overlap
SELECT timescaledb_experimental.add_policies('mat_m1', refresh_start_offset => 12, refresh_end_offset => 1, compress_after=>11);
ERROR:  refresh and columnstore policies overlap
--refresh and retention policy overlap
SELECT timescaledb_experimental.add_policies('mat_m1', refresh_start_offset => 12, refresh_end_offset => 1, drop_after=>11);
ERROR:  refresh and retention policies overlap
--compression and retention policy overlap
SELECT timescaledb_experimental.add_policies('mat_m1', compress_after => 10, drop_after => 10);
ERROR:  columnstore and retention policies overlap
-- Alter non existent policies
SELECT timescaledb_experimental.alter_policies('mat_m1', refresh_start_offset => 12, compress_after=>11, drop_after => 15);
ERROR:  no jobs found
ALTER materialized view mat_m1 set (timescaledb.compress = false);
SELECT add_continuous_aggregate_policy('int_tab', '1 day'::interval, 10 , '1 h'::interval);
ERROR:  "int_tab" is not a continuous aggregate
SELECT add_continuous_aggregate_policy('mat_m1', '1 day'::interval, 10 , '1 h'::interval);
ERROR:  invalid parameter value for start_offset
HINT:  Use time interval of type integer with the continuous aggregate.
SELECT add_continuous_aggregate_policy('mat_m1', '1 day'::interval, 10 );
ERROR:  function add_continuous_aggregate_policy(unknown, interval, integer) does not exist
LINE 1: SELECT add_continuous_aggregate_policy('mat_m1', '1 day'::in...
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
SELECT add_continuous_aggregate_policy('mat_m1', 10, '1 day'::interval, '1 h'::interval);
ERROR:  invalid parameter value for end_offset
HINT:  Use time interval of type integer with the continuous aggregate.
--start_interval < end_interval
SELECT add_continuous_aggregate_policy('mat_m1', 5, 10, '1h'::interval);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "integer".
--refresh window less than two buckets
SELECT add_continuous_aggregate_policy('mat_m1', 11, 10, '1h'::interval);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "integer".
SELECT add_continuous_aggregate_policy('mat_m1', 20, 10, '1h'::interval) as job_id \gset
--adding again should warn/error
SELECT add_continuous_aggregate_policy('mat_m1', 20, 10, '1h'::interval, if_not_exists=>false);
ERROR:  continuous aggregate refresh policy already exists for "mat_m1"
DETAIL:  A refresh policy with the same start and end offset already exists for continuous aggregate "mat_m1".
SELECT add_continuous_aggregate_policy('mat_m1', 20, 15, '1h'::interval, if_not_exists=>true);
ERROR:  refresh interval overlaps with an existing continuous aggregate policy on "mat_m1"
SELECT add_continuous_aggregate_policy('mat_m1', 20, 10, '1h'::interval, if_not_exists=>true);
NOTICE:  continuous aggregate refresh policy already exists for "mat_m1", skipping
DETAIL:  A refresh policy with the same start and end offset already exists for continuous aggregate "mat_m1".
 add_continuous_aggregate_policy 
---------------------------------
                              -1

-- modify config and try to add, should error out
SELECT config FROM _timescaledb_catalog.bgw_job where id = :job_id;
                             config                             
----------------------------------------------------------------
 {"end_offset": 10, "start_offset": 20, "mat_hypertable_id": 2}

SELECT hypertable_id as mat_id FROM _timescaledb_catalog.bgw_job where id = :job_id \gset
\set VERBOSITY terse
\set ON_ERROR_STOP 1
\c :TEST_DBNAME :ROLE_SUPERUSER
SET timezone TO PST8PDT;
UPDATE _timescaledb_catalog.bgw_job
SET config = jsonb_build_object('mat_hypertable_id', :mat_id)
WHERE id = :job_id;
SET ROLE :ROLE_DEFAULT_PERM_USER;
SELECT config FROM _timescaledb_catalog.bgw_job where id = :job_id;
          config          
--------------------------
 {"mat_hypertable_id": 2}

\set ON_ERROR_STOP 0
\set VERBOSITY default
SELECT add_continuous_aggregate_policy('mat_m1', 20, 10, '1h'::interval, if_not_exists=>true);
ERROR:  refresh interval overlaps with an existing continuous aggregate policy on "mat_m1"
SELECT remove_continuous_aggregate_policy('int_tab');
ERROR:  "int_tab" is not a continuous aggregate
SELECT remove_continuous_aggregate_policy('mat_m1');
 remove_continuous_aggregate_policy 
------------------------------------
 

-- add with NULL offset, readd with NULL offset
SELECT add_continuous_aggregate_policy('mat_m1', 20, NULL, '1h'::interval, if_not_exists=>true);
 add_continuous_aggregate_policy 
---------------------------------
                            1010

SELECT add_continuous_aggregate_policy('mat_m1', 20, NULL, '1h'::interval, if_not_exists=>true); -- same param values, so we get a NOTICE
NOTICE:  continuous aggregate refresh policy already exists for "mat_m1", skipping
DETAIL:  A refresh policy with the same start and end offset already exists for continuous aggregate "mat_m1".
 add_continuous_aggregate_policy 
---------------------------------
                              -1

SELECT add_continuous_aggregate_policy('mat_m1', NULL, NULL, '1h'::interval, if_not_exists=>true); -- different values, so we get a WARNING
ERROR:  refresh interval overlaps with an existing continuous aggregate policy on "mat_m1"
SELECT remove_continuous_aggregate_policy('mat_m1');
 remove_continuous_aggregate_policy 
------------------------------------
 

SELECT add_continuous_aggregate_policy('mat_m1', NULL, 20, '1h'::interval, if_not_exists=>true);
 add_continuous_aggregate_policy 
---------------------------------
                            1011

SELECT add_continuous_aggregate_policy('mat_m1', NULL, 20, '1h'::interval, if_not_exists=>true);
NOTICE:  continuous aggregate refresh policy already exists for "mat_m1", skipping
DETAIL:  A refresh policy with the same start and end offset already exists for continuous aggregate "mat_m1".
 add_continuous_aggregate_policy 
---------------------------------
                              -1

SELECT add_continuous_aggregate_policy('mat_m1', NULL, NULL, '1h'::interval, if_not_exists=>true);
ERROR:  refresh interval overlaps with an existing continuous aggregate policy on "mat_m1"
SELECT remove_continuous_aggregate_policy('mat_m1');
 remove_continuous_aggregate_policy 
------------------------------------
 

--this one will fail
SELECT remove_continuous_aggregate_policy('mat_m1');
ERROR:  continuous aggregate policy not found for "mat_m1"
SELECT remove_continuous_aggregate_policy('mat_m1', if_not_exists=>true);
NOTICE:  continuous aggregate policy not found for "mat_m1", skipping
 remove_continuous_aggregate_policy 
------------------------------------
 

--now try to add a policy as a different user than the one that created the cagg
--should fail
SET ROLE :ROLE_DEFAULT_PERM_USER_2;
SELECT add_continuous_aggregate_policy('mat_m1', 20, 10, '1h'::interval) as job_id ;
ERROR:  must be owner of continuous aggregate "mat_m1"
\set VERBOSITY terse
\set ON_ERROR_STOP 1
SET ROLE :ROLE_DEFAULT_PERM_USER;
DROP MATERIALIZED VIEW mat_m1;
--- code coverage tests : add policy for timestamp and date based table ---
CREATE TABLE continuous_agg_max_mat_date(time DATE);
SELECT create_hypertable('continuous_agg_max_mat_date', 'time');
            create_hypertable             
------------------------------------------
 (5,public,continuous_agg_max_mat_date,t)

CREATE MATERIALIZED VIEW max_mat_view_date
	WITH (timescaledb.continuous, timescaledb.materialized_only=true)
	AS SELECT time_bucket('7 days', time)
		FROM continuous_agg_max_mat_date
		GROUP BY 1 WITH NO DATA;
\set ON_ERROR_STOP 0
\set VERBOSITY default
-- Test 1 step policy for timestamp type buckets
ALTER materialized view max_mat_view_date set (timescaledb.compress = true);
NOTICE:  defaulting compress_orderby to time_bucket
-- Only works for cagg
SELECT timescaledb_experimental.add_policies('continuous_agg_max_mat_date', refresh_start_offset => '1 day'::interval, refresh_end_offset => '2 day'::interval, compress_after => '20 days'::interval, drop_after => '25 days'::interval);
ERROR:  "continuous_agg_max_mat_date" is not a continuous aggregate
SELECT timescaledb_experimental.show_policies('continuous_agg_max_mat_date');
ERROR:  "continuous_agg_max_mat_date" is not a continuous aggregate
SELECT timescaledb_experimental.alter_policies('continuous_agg_max_mat_date', compress_after=>'16 days'::interval);
ERROR:  "continuous_agg_max_mat_date" is not a continuous aggregate
SELECT timescaledb_experimental.remove_policies('continuous_agg_max_mat_date', false, 'policy_refresh_continuous_aggregate');
ERROR:  "continuous_agg_max_mat_date" is not a continuous aggregate
-- No policy is added if one errors out
SELECT timescaledb_experimental.add_policies('max_mat_view_date', refresh_start_offset => '1 day'::interval, refresh_end_offset => '2 day'::interval, compress_after => '20 days'::interval, drop_after => '25 days'::interval);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "date".
SELECT timescaledb_experimental.show_policies('max_mat_view_date');
 show_policies 
---------------

-- Create open ended refresh_policy
SELECT timescaledb_experimental.add_policies('max_mat_view_date', refresh_end_offset => '2 day'::interval);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                     show_policies                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": "@ 2 days", "refresh_start_offset": null}

SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false, 'policy_refresh_continuous_aggregate');
 remove_policies 
-----------------
 t

SELECT timescaledb_experimental.add_policies('max_mat_view_date', refresh_end_offset => '2 day'::interval, refresh_start_offset=>'-infinity');
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                     show_policies                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": "@ 2 days", "refresh_start_offset": null}

SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false, 'policy_refresh_continuous_aggregate');
 remove_policies 
-----------------
 t

SELECT timescaledb_experimental.add_policies('max_mat_view_date', refresh_start_offset => '2 day'::interval);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                     show_policies                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": null, "refresh_start_offset": "@ 2 days"}

SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false, 'policy_refresh_continuous_aggregate');
 remove_policies 
-----------------
 t

SELECT timescaledb_experimental.add_policies('max_mat_view_date', refresh_start_offset => '2 day'::interval, refresh_end_offset=>'infinity');
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                     show_policies                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": null, "refresh_start_offset": "@ 2 days"}

SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false, 'policy_refresh_continuous_aggregate');
 remove_policies 
-----------------
 t

-- Open ended at both sides, for code coverage
SELECT timescaledb_experimental.add_policies('max_mat_view_date', refresh_end_offset => 'infinity', refresh_start_offset => '-infinity');
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                  show_policies                                                                   
--------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": null, "refresh_start_offset": null}

SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false, 'policy_refresh_continuous_aggregate');
 remove_policies 
-----------------
 t

-- All policies are added in one step
SELECT timescaledb_experimental.add_policies('max_mat_view_date', refresh_start_offset => '15 days'::interval, refresh_end_offset => '1 day'::interval, compress_after => '20 days'::interval, drop_after => '25 days'::interval);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                        show_policies                                                                         
--------------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": "@ 20 days", "compress_interval": "@ 12 hours"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": "@ 1 day", "refresh_start_offset": "@ 15 days"}
 {"drop_after": "@ 25 days", "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Alter policies
SELECT timescaledb_experimental.alter_policies('max_mat_view_date', refresh_start_offset => '16 days'::interval, compress_after=>'26 days'::interval, drop_after => '40 days'::interval);
 alter_policies 
----------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                        show_policies                                                                         
--------------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": "@ 26 days", "compress_interval": "@ 12 hours"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": "@ 1 day", "refresh_start_offset": "@ 16 days"}
 {"drop_after": "@ 40 days", "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

--Alter refresh_policy to make it open ended
SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false, 'policy_retention', 'policy_compression');
 remove_policies 
-----------------
 t

SELECT timescaledb_experimental.alter_policies('max_mat_view_date', refresh_start_offset =>'-infinity');
 alter_policies 
----------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                     show_policies                                                                     
-------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": "@ 1 day", "refresh_start_offset": null}

SELECT timescaledb_experimental.alter_policies('max_mat_view_date', refresh_end_offset =>'infinity', refresh_start_offset =>'5 days'::interval);
 alter_policies 
----------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                     show_policies                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": null, "refresh_start_offset": "@ 5 days"}

--Cross policy checks
-- Refresh and compression policies overlap
SELECT timescaledb_experimental.add_policies('max_mat_view_date', compress_after => '20 days'::interval, drop_after => '25 days'::interval);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.alter_policies('max_mat_view_date', compress_after=> '4 days'::interval);
ERROR:  refresh and columnstore policies overlap
SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                     show_policies                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": "@ 20 days", "compress_interval": "@ 12 hours"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": null, "refresh_start_offset": "@ 5 days"}
 {"drop_after": "@ 25 days", "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Refresh and retention policies overlap
SELECT timescaledb_experimental.alter_policies('max_mat_view_date', refresh_start_offset =>'5 days'::interval, drop_after=> '4 days'::interval);
ERROR:  refresh and retention policies overlap
SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                     show_policies                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": "@ 20 days", "compress_interval": "@ 12 hours"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": null, "refresh_start_offset": "@ 5 days"}
 {"drop_after": "@ 25 days", "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

--Do not allow refreshed data to be deleted
SELECT add_retention_policy('continuous_agg_max_mat_date', '25 days'::interval);
 add_retention_policy 
----------------------
                 1027

SELECT timescaledb_experimental.alter_policies('max_mat_view_date', refresh_start_offset =>'25 days'::interval);
ERROR:  refresh policy of continuous aggregate and retention policy of underlying hypertable overlap
SELECT remove_retention_policy('continuous_agg_max_mat_date');
 remove_retention_policy 
-------------------------
 

-- Remove one or more policy
-- Code coverage: no policy names provided
SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false);
 remove_policies 
-----------------
 f

-- Code coverage: incorrect name of policy
SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false, 'refresh_policy');
NOTICE:  No relevant policy found
 remove_policies 
-----------------
 f

SELECT timescaledb_experimental.remove_policies('max_mat_view_date', false, 'policy_refresh_continuous_aggregate', 'policy_compression');
 remove_policies 
-----------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                          show_policies                                          
-------------------------------------------------------------------------------------------------
 {"drop_after": "@ 25 days", "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Add one policy
SELECT timescaledb_experimental.add_policies('max_mat_view_date', refresh_start_offset => '15 day'::interval, refresh_end_offset => '1 day'::interval);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('max_mat_view_date');
                                                                        show_policies                                                                         
--------------------------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": "@ 1 day", "refresh_start_offset": "@ 15 days"}
 {"drop_after": "@ 25 days", "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Remove all policies
SELECT * FROM timescaledb_experimental.policies ORDER BY relation_name, proc_name;
   relation_name   | relation_schema | schedule_interval |      proc_schema       |              proc_name              |                                     config                                     |   hypertable_schema   |      hypertable_name       
-------------------+-----------------+-------------------+------------------------+-------------------------------------+--------------------------------------------------------------------------------+-----------------------+----------------------------
 max_mat_view_date | public          | @ 1 hour          | _timescaledb_functions | policy_refresh_continuous_aggregate | {"end_offset": "@ 1 day", "start_offset": "@ 15 days", "mat_hypertable_id": 6} | _timescaledb_internal | _materialized_hypertable_6
 max_mat_view_date | public          | @ 1 day           | _timescaledb_functions | policy_retention                    | {"drop_after": "@ 25 days", "hypertable_id": 6}                                | _timescaledb_internal | _materialized_hypertable_6

SELECT timescaledb_experimental.remove_all_policies(NULL); -- should fail
 remove_all_policies 
---------------------
 f

SELECT timescaledb_experimental.remove_all_policies('continuous_agg_max_mat_date'); -- should fail
ERROR:  "continuous_agg_max_mat_date" is not a continuous aggregate
SELECT timescaledb_experimental.remove_all_policies('max_mat_view_date', false);
 remove_all_policies 
---------------------
 t

SELECT timescaledb_experimental.remove_all_policies('max_mat_view_date', false); -- should fail
 remove_all_policies 
---------------------
 f

CREATE OR REPLACE FUNCTION custom_func(jobid int, args jsonb) RETURNS RECORD LANGUAGE SQL AS
$$
  VALUES($1, $2, 'custom_func');
$$;
 -- inject custom job
SELECT add_job('custom_func','1h', config:='{"type":"function"}'::jsonb, initial_start => '2000-01-01 00:00:00+00'::timestamptz) AS job_id \gset
SELECT _timescaledb_functions.alter_job_set_hypertable_id( :job_id, 'max_mat_view_date'::regclass);
 alter_job_set_hypertable_id 
-----------------------------
                        1029

SELECT * FROM timescaledb_information.jobs WHERE job_id != 1 ORDER BY 1;
 job_id |      application_name      | schedule_interval | max_runtime | max_retries | retry_period | proc_schema |  proc_name  |       owner       | scheduled | fixed_schedule |        config        |          next_start          |        initial_start         | hypertable_schema |  hypertable_name  | check_schema | check_name 
--------+----------------------------+-------------------+-------------+-------------+--------------+-------------+-------------+-------------------+-----------+----------------+----------------------+------------------------------+------------------------------+-------------------+-------------------+--------------+------------
   1029 | User-Defined Action [1029] | @ 1 hour          | @ 0         |          -1 | @ 5 mins     | public      | custom_func | default_perm_user | t         | t              | {"type": "function"} | Fri Dec 31 16:00:00 1999 PST | Fri Dec 31 16:00:00 1999 PST | public            | max_mat_view_date |              | 

SELECT timescaledb_experimental.remove_all_policies('max_mat_view_date', true); -- ignore custom job
NOTICE:  Ignoring custom job
 remove_all_policies 
---------------------
 t

SELECT delete_job(:job_id);
 delete_job 
------------
 

DROP FUNCTION custom_func;
SELECT timescaledb_experimental.show_policies('max_mat_view_date');
 show_policies 
---------------

ALTER materialized view max_mat_view_date set (timescaledb.compress = false);
SELECT add_continuous_aggregate_policy('max_mat_view_date', '2 days', 10, '1 day'::interval);
ERROR:  invalid parameter value for end_offset
HINT:  Use time interval with a continuous aggregate using timestamp-based time bucket.
--start_interval < end_interval
SELECT add_continuous_aggregate_policy('max_mat_view_date', '1 day'::interval, '2 days'::interval , '1 day'::interval) ;
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "date".
--interval less than two buckets
SELECT add_continuous_aggregate_policy('max_mat_view_date', '7 days', '1 day', '1 day'::interval);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "date".
SELECT add_continuous_aggregate_policy('max_mat_view_date', '14 days', '1 day', '1 day'::interval);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "date".
SELECT add_continuous_aggregate_policy('max_mat_view_date', '13 days', '-10 hours', '1 day'::interval);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "date".
\set VERBOSITY terse
\set ON_ERROR_STOP 1
-- Negative start offset gives two bucket window:
SELECT add_continuous_aggregate_policy('max_mat_view_date', '13 days', '-1 day', '1 day'::interval);
 add_continuous_aggregate_policy 
---------------------------------
                            1030

SELECT remove_continuous_aggregate_policy('max_mat_view_date');
 remove_continuous_aggregate_policy 
------------------------------------
 

-- Both offsets NULL:
SELECT add_continuous_aggregate_policy('max_mat_view_date', NULL, NULL, '1 day'::interval);
 add_continuous_aggregate_policy 
---------------------------------
                            1031

SELECT remove_continuous_aggregate_policy('max_mat_view_date');
 remove_continuous_aggregate_policy 
------------------------------------
 

SELECT add_continuous_aggregate_policy('max_mat_view_date', '15 days', '1 day', '1 day'::interval) as job_id \gset
SELECT config FROM _timescaledb_catalog.bgw_job
WHERE id = :job_id;
                                     config                                     
--------------------------------------------------------------------------------
 {"end_offset": "@ 1 day", "start_offset": "@ 15 days", "mat_hypertable_id": 6}

INSERT INTO continuous_agg_max_mat_date
	SELECT generate_series('2019-09-01'::date, '2019-09-10'::date, '1 day');
--- to prevent NOTICES set message level to warning
SET client_min_messages TO warning;
CALL run_job(:job_id);
RESET client_min_messages;
DROP MATERIALIZED VIEW max_mat_view_date;
CREATE TABLE continuous_agg_timestamp(time TIMESTAMP);
SELECT create_hypertable('continuous_agg_timestamp', 'time');
WARNING:  column type "timestamp without time zone" used for "time" does not follow best practices
           create_hypertable           
---------------------------------------
 (8,public,continuous_agg_timestamp,t)

CREATE MATERIALIZED VIEW max_mat_view_timestamp
	WITH (timescaledb.continuous, timescaledb.materialized_only=true)
	AS SELECT time_bucket('7 days', time)
		FROM continuous_agg_timestamp
		GROUP BY 1 WITH NO DATA;
--the start offset overflows the smallest time value, but is capped at
--the min value
SELECT add_continuous_aggregate_policy('max_mat_view_timestamp', '1000000 years', '1 day' , '1 h'::interval);
 add_continuous_aggregate_policy 
---------------------------------
                            1033

SELECT remove_continuous_aggregate_policy('max_mat_view_timestamp');
 remove_continuous_aggregate_policy 
------------------------------------
 

\set ON_ERROR_STOP 0
\set VERBOSITY default
--start and end offset capped at the lowest time value, which means
--zero size window
SELECT add_continuous_aggregate_policy('max_mat_view_timestamp', '1000000 years', '900000 years' , '1 h'::interval);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "timestamp without time zone".
SELECT add_continuous_aggregate_policy('max_mat_view_timestamp', '301 days', '10 months' , '1 h'::interval);
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "timestamp without time zone".
\set VERBOSITY terse
\set ON_ERROR_STOP 1
SELECT add_continuous_aggregate_policy('max_mat_view_timestamp', '15 days', '1 h'::interval , '1 h'::interval) as job_id \gset
--- to prevent NOTICES set message level to warning
SET client_min_messages TO warning;
CALL run_job(:job_id);
RESET client_min_messages ;
SELECT config FROM _timescaledb_catalog.bgw_job
WHERE id = :job_id;
                                     config                                      
---------------------------------------------------------------------------------
 {"end_offset": "@ 1 hour", "start_offset": "@ 15 days", "mat_hypertable_id": 9}

\c :TEST_DBNAME :ROLE_SUPERUSER
SET timezone TO PST8PDT;
UPDATE _timescaledb_catalog.bgw_job
SET config = jsonb_build_object('mat_hypertable_id', :mat_id)
WHERE id = :job_id;
SET ROLE :ROLE_DEFAULT_PERM_USER;
SELECT config FROM _timescaledb_catalog.bgw_job where id = :job_id;
          config          
--------------------------
 {"mat_hypertable_id": 2}

\set ON_ERROR_STOP 0
SELECT add_continuous_aggregate_policy('max_mat_view_timestamp', '15 day', '1 day', '1h'::interval, if_not_exists=>true);
ERROR:  refresh interval overlaps with an existing continuous aggregate policy on "max_mat_view_timestamp"
SELECT add_continuous_aggregate_policy('max_mat_view_timestamp', 'xyz', '1 day', '1h'::interval, if_not_exists=>true);
ERROR:  invalid input syntax for type interval: "xyz"
\set ON_ERROR_STOP 1
DROP MATERIALIZED VIEW max_mat_view_timestamp;
--smallint table
CREATE TABLE smallint_tab (a smallint);
SELECT table_name FROM create_hypertable('smallint_tab', 'a', chunk_time_interval=> 10);
  table_name  
--------------
 smallint_tab

CREATE OR REPLACE FUNCTION integer_now_smallint_tab() returns smallint LANGUAGE SQL STABLE as $$ SELECT coalesce(max(a)::smallint, 0::smallint) FROM smallint_tab ; $$;
SELECT set_integer_now_func('smallint_tab', 'integer_now_smallint_tab');
 set_integer_now_func 
----------------------
 

CREATE MATERIALIZED VIEW mat_smallint( a, countb )
WITH (timescaledb.continuous, timescaledb.materialized_only=true)
as
SELECT time_bucket( SMALLINT '1', a) , count(*)
FROM smallint_tab
GROUP BY 1 WITH NO DATA;
\set ON_ERROR_STOP 0
\set VERBOSITY default
-- Test 1 step policy for smallint type buckets
ALTER materialized view mat_smallint set (timescaledb.compress = true);
NOTICE:  defaulting compress_orderby to a
-- All policies are added in one step
SELECT timescaledb_experimental.add_policies('mat_smallint', refresh_start_offset => 10::smallint, refresh_end_offset => 1::smallint, compress_after => 11::smallint, drop_after => 20::smallint);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('mat_smallint');
                                                                show_policies                                                                
---------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": 11, "compress_interval": "@ 1 day"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": 1, "refresh_start_offset": 10}
 {"drop_after": 20, "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Alter policies
SELECT timescaledb_experimental.alter_policies('mat_smallint',  refresh_start_offset => 11::smallint, compress_after=>13::smallint, drop_after => 25::smallint);
 alter_policies 
----------------
 t

SELECT timescaledb_experimental.show_policies('mat_smallint');
                                                                show_policies                                                                
---------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": 13, "compress_interval": "@ 1 day"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": 1, "refresh_start_offset": 11}
 {"drop_after": 25, "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

SELECT timescaledb_experimental.remove_all_policies('mat_smallint', false);
 remove_all_policies 
---------------------
 t

ALTER materialized view mat_smallint set (timescaledb.compress = false);
SELECT add_continuous_aggregate_policy('mat_smallint', 15, 0 , '1 h'::interval);
ERROR:  invalid parameter value for start_offset
HINT:  Use time interval of type smallint with the continuous aggregate.
SELECT add_continuous_aggregate_policy('mat_smallint', 98898::smallint , 0::smallint, '1 h'::interval);
ERROR:  smallint out of range
SELECT add_continuous_aggregate_policy('mat_smallint', 5::smallint, 10::smallint , '1 h'::interval) as job_id \gset
ERROR:  policy refresh window too small
DETAIL:  The start and end offsets must cover at least two buckets in the valid time range of type "smallint".
\set VERBOSITY terse
\set ON_ERROR_STOP 1
SELECT add_continuous_aggregate_policy('mat_smallint', 15::smallint, 0::smallint , '1 h'::interval) as job_id \gset
INSERT INTO smallint_tab VALUES(5);
INSERT INTO smallint_tab VALUES(10);
INSERT INTO smallint_tab VALUES(20);
CALL run_job(:job_id);
SELECT * FROM mat_smallint ORDER BY 1;
 a  | countb 
----+--------
  5 |      1
 10 |      1

--remove all the data--
TRUNCATE table smallint_tab;
CALL refresh_continuous_aggregate('mat_smallint', NULL, NULL);
SELECT * FROM mat_smallint ORDER BY 1;
 a | countb 
---+--------

-- Case 1: overflow by subtracting from PG_INT16_MIN
--overflow start_interval, end_interval [-32768, -32768)
SELECT remove_continuous_aggregate_policy('mat_smallint');
 remove_continuous_aggregate_policy 
------------------------------------
 

INSERT INTO smallint_tab VALUES( -32768 );
SELECT integer_now_smallint_tab();
 integer_now_smallint_tab 
--------------------------
                   -32768

SELECT add_continuous_aggregate_policy('mat_smallint', 10::smallint, 5::smallint , '1 h'::interval) as job_id \gset
\set ON_ERROR_STOP 0
CALL run_job(:job_id);
ERROR:  invalid refresh window
\set ON_ERROR_STOP 1
SELECT * FROM mat_smallint ORDER BY 1;
 a | countb 
---+--------

-- overflow start_interval. now this runs as range is capped [-32768, -32765)
INSERT INTO smallint_tab VALUES( -32760 );
SELECT maxval, maxval - 10, maxval -5 FROM integer_now_smallint_tab() as maxval;
 maxval | ?column? | ?column? 
--------+----------+----------
 -32760 |   -32770 |   -32765

CALL run_job(:job_id);
SELECT * FROM mat_smallint ORDER BY 1;
   a    | countb 
--------+--------
 -32768 |      1

--remove all the data--
TRUNCATE table smallint_tab;
CALL refresh_continuous_aggregate('mat_smallint', NULL, NULL);
SELECT * FROM mat_smallint ORDER BY 1;
 a | countb 
---+--------

-- Case 2: overflow by subtracting from PG_INT16_MAX
--overflow start and end . will fail as range is [32767, 32767]
SELECT remove_continuous_aggregate_policy('mat_smallint');
 remove_continuous_aggregate_policy 
------------------------------------
 

INSERT INTO smallint_tab VALUES( 32766 );
INSERT INTO smallint_tab VALUES( 32767 );
SELECT maxval, maxval - (-1), maxval - (-2) FROM integer_now_smallint_tab() as maxval;
 maxval | ?column? | ?column? 
--------+----------+----------
  32767 |    32768 |    32769

SELECT add_continuous_aggregate_policy('mat_smallint', -1::smallint, -3::smallint , '1 h'::interval) as job_id \gset
\set ON_ERROR_STOP 0
CALL run_job(:job_id);
ERROR:  invalid refresh window
\set ON_ERROR_STOP 1
SELECT * FROM mat_smallint ORDER BY 1;
 a | countb 
---+--------

SELECT remove_continuous_aggregate_policy('mat_smallint');
 remove_continuous_aggregate_policy 
------------------------------------
 

--overflow end . will work range is [32765, 32767)
SELECT maxval, maxval - (1), maxval - (-2) FROM integer_now_smallint_tab() as maxval;
 maxval | ?column? | ?column? 
--------+----------+----------
  32767 |    32766 |    32769

SELECT add_continuous_aggregate_policy('mat_smallint', 1::smallint, -3::smallint , '1 h'::interval) as job_id \gset
\set ON_ERROR_STOP 0
CALL run_job(:job_id);
SELECT * FROM mat_smallint ORDER BY 1;
   a   | countb 
-------+--------
 32766 |      1

-- tests for interval argument conversions
--
\set ON_ERROR_STOP 0
SELECT add_continuous_aggregate_policy('mat_smallint', 15, 10, '1h'::interval, if_not_exists=>true);
ERROR:  invalid parameter value for start_offset
SELECT add_continuous_aggregate_policy('mat_smallint', '15', 10, '1h'::interval, if_not_exists=>true);
ERROR:  invalid parameter value for end_offset
SELECT add_continuous_aggregate_policy('mat_smallint', '15', '10', '1h'::interval, if_not_exists=>true);
 add_continuous_aggregate_policy 
---------------------------------
                            1045

\set ON_ERROR_STOP 1
--bigint table
CREATE TABLE bigint_tab (a bigint);
SELECT table_name FROM create_hypertable('bigint_tab', 'a', chunk_time_interval=> 10);
 table_name 
------------
 bigint_tab

CREATE OR REPLACE FUNCTION integer_now_bigint_tab() returns bigint LANGUAGE SQL STABLE as $$ SELECT 20::bigint $$;
SELECT set_integer_now_func('bigint_tab', 'integer_now_bigint_tab');
 set_integer_now_func 
----------------------
 

CREATE MATERIALIZED VIEW mat_bigint( a, countb )
WITH (timescaledb.continuous, timescaledb.materialized_only=true)
as
SELECT time_bucket( BIGINT '1', a) , count(*)
FROM bigint_tab
GROUP BY 1 WITH NO DATA;
-- Test 1 step policy for bigint type buckets
ALTER materialized view mat_bigint set (timescaledb.compress = true);
NOTICE:  defaulting compress_orderby to a
-- All policies are added in one step
SELECT timescaledb_experimental.add_policies('mat_bigint', refresh_start_offset => 10::bigint, refresh_end_offset => 1::bigint, compress_after => 11::bigint, drop_after => 20::bigint);
 add_policies 
--------------
 t

SELECT timescaledb_experimental.show_policies('mat_bigint');
                                                                show_policies                                                                
---------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": 11, "compress_interval": "@ 1 day"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": 1, "refresh_start_offset": 10}
 {"drop_after": 20, "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

-- Alter policies
SELECT timescaledb_experimental.alter_policies('mat_bigint',  refresh_start_offset => 11::bigint, compress_after=>13::bigint, drop_after => 25::bigint);
 alter_policies 
----------------
 t

SELECT timescaledb_experimental.show_policies('mat_bigint');
                                                                show_policies                                                                
---------------------------------------------------------------------------------------------------------------------------------------------
 {"policy_name": "policy_compression", "compress_after": 13, "compress_interval": "@ 1 day"}
 {"policy_name": "policy_refresh_continuous_aggregate", "refresh_interval": "@ 1 hour", "refresh_end_offset": 1, "refresh_start_offset": 11}
 {"drop_after": 25, "policy_name": "policy_retention", "retention_interval": "@ 1 day"}

SELECT timescaledb_experimental.remove_all_policies('mat_bigint', false);
 remove_all_policies 
---------------------
 t

ALTER materialized view mat_bigint set (timescaledb.compress = false);
\set ON_ERROR_STOP 0
SELECT add_continuous_aggregate_policy('mat_bigint', 5::bigint, 10::bigint , '1 h'::interval) ;
ERROR:  policy refresh window too small
\set ON_ERROR_STOP 1
SELECT add_continuous_aggregate_policy('mat_bigint', 15::bigint, 0::bigint , '1 h'::interval) as job_mid \gset
INSERT INTO bigint_tab VALUES(5);
INSERT INTO bigint_tab VALUES(10);
INSERT INTO bigint_tab VALUES(20);
CALL run_job(:job_mid);
SELECT * FROM mat_bigint ORDER BY 1;
 a  | countb 
----+--------
  5 |      1
 10 |      1

-- test NULL for end
SELECT remove_continuous_aggregate_policy('mat_bigint');
 remove_continuous_aggregate_policy 
------------------------------------
 

SELECT add_continuous_aggregate_policy('mat_bigint', 1::smallint, NULL , '1 h'::interval) as job_id \gset
INSERT INTO bigint_tab VALUES(500);
CALL run_job(:job_id);
SELECT * FROM mat_bigint WHERE a>100 ORDER BY 1;
  a  | countb 
-----+--------
 500 |      1

ALTER MATERIALIZED VIEW mat_bigint SET (timescaledb.compress);
NOTICE:  defaulting compress_orderby to a
ALTER MATERIALIZED VIEW mat_smallint SET (timescaledb.compress);
NOTICE:  defaulting compress_orderby to a
-- With immutable compressed chunks, these policies would fail by overlapping the refresh window
SELECT add_compression_policy('mat_smallint', -4::smallint);
 add_compression_policy 
------------------------
                   1054

SELECT remove_compression_policy('mat_smallint');
 remove_compression_policy 
---------------------------
 t

SELECT add_compression_policy('mat_bigint', 0::bigint);
 add_compression_policy 
------------------------
                   1055

SELECT remove_compression_policy('mat_bigint');
 remove_compression_policy 
---------------------------
 t

-- End previous limitation tests
SELECT add_compression_policy('mat_smallint', 5::smallint);
 add_compression_policy 
------------------------
                   1056

SELECT add_compression_policy('mat_bigint', 20::bigint);
 add_compression_policy 
------------------------
                   1057

-- end of coverage tests
--TEST continuous aggregate + compression policy on caggs
CREATE TABLE metrics (
    time timestamptz NOT NULL,
    device_id int,
    device_id_peer int,
    v0 int,
    v1 int,
    v2 float,
    v3 float
);
SELECT create_hypertable('metrics', 'time');
   create_hypertable   
-----------------------
 (18,public,metrics,t)

INSERT INTO metrics (time, device_id, device_id_peer, v0, v1, v2, v3)
SELECT time,
    device_id,
    0,
    device_id + 1,
    device_id + 2,
    0.5,
    NULL
FROM generate_series('2000-01-01 0:00:00+0'::timestamptz, '2000-01-02 23:55:00+0', '20m') gtime (time),
    generate_series(1, 2, 1) gdevice (device_id);
ALTER TABLE metrics SET ( timescaledb.compress );
SELECT compress_chunk(ch) FROM show_chunks('metrics') ch;
              compress_chunk              
------------------------------------------
 _timescaledb_internal._hyper_18_19_chunk

CREATE MATERIALIZED VIEW metrics_cagg WITH (timescaledb.continuous,
  timescaledb.materialized_only = true)
AS
SELECT time_bucket('1 day', time) as dayb, device_id,
       sum(v0), avg(v3)
FROM metrics
GROUP BY 1, 2
WITH NO DATA;
-- this was previously crashing
SELECT add_continuous_aggregate_policy('metrics_cagg', '7 day'::interval, NULL, '1 h'::interval, if_not_exists => true);
 add_continuous_aggregate_policy 
---------------------------------
                            1058

\set ON_ERROR_STOP 0
SELECT add_continuous_aggregate_policy('metrics_cagg', '7 day'::interval, '1 day'::interval, '1 h'::interval, if_not_exists => true);
ERROR:  refresh interval overlaps with an existing continuous aggregate policy on "metrics_cagg"
SELECT remove_continuous_aggregate_policy('metrics_cagg');
 remove_continuous_aggregate_policy 
------------------------------------
 

SELECT add_continuous_aggregate_policy('metrics_cagg', NULL, '1 day'::interval, '1h'::interval, if_not_exists=>true);
 add_continuous_aggregate_policy 
---------------------------------
                            1059

SELECT add_continuous_aggregate_policy('metrics_cagg', NULL, '1 day'::interval, '1h'::interval, if_not_exists=>true); -- same param values, so we get a NOTICE
NOTICE:  continuous aggregate refresh policy already exists for "metrics_cagg", skipping
 add_continuous_aggregate_policy 
---------------------------------
                              -1

SELECT add_continuous_aggregate_policy('metrics_cagg', NULL, NULL, '1h'::interval, if_not_exists=>true); -- different values, so we get a WARNING
ERROR:  refresh interval overlaps with an existing continuous aggregate policy on "metrics_cagg"
SELECT remove_continuous_aggregate_policy('metrics_cagg');
 remove_continuous_aggregate_policy 
------------------------------------
 

--can set compression policy only after setting up refresh policy --
SELECT add_compression_policy('metrics_cagg', '1 day'::interval);
ERROR:  setup a refresh policy for "metrics_cagg" before setting up a columnstore policy
--can set compression policy only after enabling compression --
SELECT add_continuous_aggregate_policy('metrics_cagg', '7 day'::interval, '1 day'::interval, '1 h'::interval) as "REFRESH_JOB" \gset
SELECT add_compression_policy('metrics_cagg', '8 day'::interval) AS "COMP_JOB" ;
ERROR:  columnstore not enabled on continuous aggregate "metrics_cagg"
ALTER MATERIALIZED VIEW metrics_cagg SET (timescaledb.compress);
NOTICE:  defaulting compress_orderby to dayb,device_id
--cannot use compress_created_before with cagg
SELECT add_compression_policy('metrics_cagg', compress_created_before => '8 day'::interval) AS "COMP_JOB" ;
ERROR:  cannot use "compress_created_before" with continuous aggregate "metrics_cagg" 
\set ON_ERROR_STOP 1
SELECT add_compression_policy('metrics_cagg', '8 day'::interval) AS "COMP_JOB" ;
 COMP_JOB 
----------
     1061

SELECT remove_compression_policy('metrics_cagg');
 remove_compression_policy 
---------------------------
 t

SELECT add_compression_policy('metrics_cagg', '8 day'::interval) AS "COMP_JOB" \gset
--verify that jobs were added for the policies ---
SELECT materialization_hypertable_name AS "MAT_TABLE_NAME",
       view_name AS "VIEW_NAME"
FROM timescaledb_information.continuous_aggregates
WHERE view_name = 'metrics_cagg' \gset
SELECT count(*) FROM timescaledb_information.jobs
WHERE hypertable_name = :'VIEW_NAME';
 count 
-------
     2

--exec the cagg compression job --
CALL refresh_continuous_aggregate('metrics_cagg', NULL, '2001-02-01 00:00:00+0');
CALL run_job(:COMP_JOB);
SELECT count(*), count(*) FILTER ( WHERE is_compressed is TRUE  )
FROM timescaledb_information.chunks
WHERE hypertable_name = :'MAT_TABLE_NAME' ORDER BY 1;
 count | count 
-------+-------
     1 |     1

--add some new data into metrics_cagg so that cagg policy job has something to do
INSERT INTO metrics (time, device_id, device_id_peer, v0, v1, v2, v3)
SELECT now() - '5 day'::interval, 102, 0, 10, 10, 10, 10;
CALL run_job(:REFRESH_JOB);
--now we have a new chunk and it is not compressed
SELECT count(*), count(*) FILTER ( WHERE is_compressed is TRUE  )
FROM timescaledb_information.chunks
WHERE hypertable_name = :'MAT_TABLE_NAME' ORDER BY 1;
 count | count 
-------+-------
     2 |     1

--verify that both jobs are dropped when view is dropped
DROP MATERIALIZED VIEW metrics_cagg;
NOTICE:  drop cascades to 2 other objects
SELECT count(*) FROM timescaledb_information.jobs
WHERE hypertable_name = :'VIEW_NAME';
 count 
-------
     0

-- add test case for issue 4252
CREATE TABLE IF NOT EXISTS sensor_data(
time TIMESTAMPTZ NOT NULL,
sensor_id INTEGER,
temperature DOUBLE PRECISION,
cpu DOUBLE PRECISION);
SELECT create_hypertable('sensor_data','time');
     create_hypertable     
---------------------------
 (22,public,sensor_data,t)

INSERT INTO sensor_data(time, sensor_id, cpu, temperature)
SELECT
time,
sensor_id,
extract(dow from time) AS cpu,
extract(doy from time) AS temperature
FROM
generate_series('2022-05-05'::timestamp at time zone 'UTC' - interval '6 weeks', '2022-05-05'::timestamp at time zone 'UTC', interval '5 hours') as g1(time),
generate_series(1,1000,1) as g2(sensor_id);
CREATE materialized view deals_best_weekly
WITH (timescaledb.continuous) AS
SELECT
time_bucket('7 days', "time") AS bucket,
avg(temperature) AS avg_temp,
max(cpu) AS max_rating
FROM sensor_data
GROUP BY bucket
WITH NO DATA;
CREATE materialized view deals_best_daily
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 day', "time") AS bucket,
avg(temperature) AS avg_temp,
max(cpu) AS max_rating
FROM sensor_data
GROUP BY bucket
WITH NO DATA;
ALTER materialized view deals_best_weekly set (timescaledb.materialized_only=true);
ALTER materialized view deals_best_daily set (timescaledb.materialized_only=true);
-- we have data from 6 weeks before to May 5 2022 (Thu)
CALL refresh_continuous_aggregate('deals_best_weekly', '2022-04-24', '2022-05-03');
SELECT * FROM deals_best_weekly ORDER BY bucket;
            bucket            |     avg_temp     | max_rating 
------------------------------+------------------+------------
 Sun Apr 24 17:00:00 2022 PDT | 117.764705882353 |          6

CALL refresh_continuous_aggregate('deals_best_daily', '2022-04-20', '2022-05-04');
SELECT * FROM deals_best_daily ORDER BY bucket LIMIT 2;
            bucket            | avg_temp | max_rating 
------------------------------+----------+------------
 Wed Apr 20 17:00:00 2022 PDT |    110.8 |          4
 Thu Apr 21 17:00:00 2022 PDT |   111.75 |          5

-- expect to get an up-to-date notice
CALL refresh_continuous_aggregate('deals_best_weekly', '2022-04-24', '2022-05-05');
NOTICE:  continuous aggregate "deals_best_weekly" is already up-to-date
SELECT * FROM deals_best_weekly ORDER BY bucket;
            bucket            |     avg_temp     | max_rating 
------------------------------+------------------+------------
 Sun Apr 24 17:00:00 2022 PDT | 117.764705882353 |          6

-- github issue 5907: segfault when creating 1-step policies on cagg
-- whose underlying hypertable has a retention policy setup
CREATE TABLE t(a integer NOT NULL, b integer);
SELECT create_hypertable('t', 'a', chunk_time_interval=> 10);
 create_hypertable 
-------------------
 (25,public,t,t)

CREATE OR REPLACE FUNCTION unix_now() returns int LANGUAGE SQL IMMUTABLE as $$ SELECT extract(epoch from now())::INT $$;
SELECT set_integer_now_func('t', 'unix_now');
 set_integer_now_func 
----------------------
 

SELECT add_retention_policy('t', 20);
 add_retention_policy 
----------------------
                 1063

CREATE MATERIALIZED VIEW cagg(a, sumb) WITH (timescaledb.continuous)
AS SELECT time_bucket(1, a), sum(b)
   FROM t GROUP BY time_bucket(1, a);
NOTICE:  continuous aggregate "cagg" is already up-to-date
SELECT timescaledb_experimental.add_policies('cagg');
 add_policies 
--------------
 f

-- Issue #6902
-- Fix timestamp out of range in a refresh policy when setting `end_offset=>NULL`
-- for a CAgg with variable sized bucket (i.e: using `time_bucket` with timezone)
CREATE TABLE issue_6902 (
  ts  TIMESTAMPTZ NOT NULL,
  temperature NUMERIC
) WITH (
  timescaledb.hypertable,
  timescaledb.partition_column='ts',
  timescaledb.chunk_interval='1 day',
  timescaledb.compress='off'
);
INSERT INTO issue_6902
SELECT t, 1 FROM generate_series(now() - interval '3 hours', now(), interval '1 minute') AS t;
CREATE MATERIALIZED VIEW issue_6902_by_hour
WITH (timescaledb.continuous) AS
SELECT
  time_bucket(INTERVAL '1 hour', ts, 'America/Sao_Paulo') AS bucket, -- using timezone
  MAX(temperature),
  MIN(temperature),
  COUNT(*)
FROM issue_6902
GROUP BY 1
WITH NO DATA;
SELECT add_continuous_aggregate_policy (
  'issue_6902_by_hour',
  start_offset => INTERVAL '3 hours',
  end_offset => NULL,
  schedule_interval => INTERVAL '12 hour',
  initial_start => now() + INTERVAL '12 hour'
) AS job_id \gset
-- 181 rows
CALL run_job(:job_id);
SELECT count(*) FROM issue_6902;
 count 
-------
   181

-- run again without any change, remain 181 rows
CALL run_job(:job_id);
SELECT count(*) FROM issue_6902;
 count 
-------
   181

-- change existing data
UPDATE issue_6902
SET temperature = temperature + 1;
-- run again without any change, remain 181 rows
CALL run_job(:job_id);
SELECT count(*) FROM issue_6902;
 count 
-------
   181

-- insert more data
INSERT INTO issue_6902
SELECT t, 1 FROM generate_series(now() - interval '3 hours', now(), interval '1 minute') AS t;
-- run again without and should have 362 rows
CALL run_job(:job_id);
SELECT count(*) FROM issue_6902;
 count 
-------
   362

-- test untyped interval error handling
CREATE TABLE m(time timestamptz) WITH (tsdb.hypertable);
NOTICE:  using column "time" as partitioning column
CREATE MATERIALIZED VIEW cagg_error WITH (tsdb.continuous)
AS SELECT time_bucket('1 day', time) FROM m GROUP BY 1;
NOTICE:  continuous aggregate "cagg_error" is already up-to-date
SELECT timescaledb_experimental.add_policies('cagg_error', drop_after => '20 days');
ERROR:  unsupported interval argument type: unknown
