-- 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.
--
-- Setup
--
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(timeout INT = -1, mock_start_time INT = 0) RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION ts_bgw_db_scheduler_test_run(timeout INT = -1, mock_start_time INT = 0) RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION ts_bgw_db_scheduler_test_wait_for_scheduler_finish() RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION ts_bgw_params_create() RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION ts_bgw_params_destroy() RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
CREATE OR REPLACE FUNCTION ts_bgw_params_reset_time(set_time BIGINT = 0, wait BOOLEAN = false) RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
--test that this all works under the community license
ALTER DATABASE :TEST_DBNAME SET timescaledb.license_key='Community';
--create a function with no permissions to execute
CREATE FUNCTION get_constant_no_perms() RETURNS INTEGER LANGUAGE SQL IMMUTABLE AS
$BODY$
    SELECT 10;
$BODY$;
REVOKE EXECUTE ON FUNCTION get_constant_no_perms() FROM PUBLIC;
\set WAIT_ON_JOB 0
\set IMMEDIATELY_SET_UNTIL 1
\set WAIT_FOR_OTHER_TO_ADVANCE 2
CREATE OR REPLACE FUNCTION ts_bgw_params_mock_wait_returns_immediately(new_val INTEGER) RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
-- Remove any default jobs, e.g., telemetry
DELETE FROM _timescaledb_catalog.bgw_job WHERE TRUE;
TRUNCATE _timescaledb_internal.bgw_job_stat;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE public.bgw_log(
    msg_no INT,
    mock_time BIGINT,
    application_name TEXT,
    msg TEXT
);
CREATE VIEW sorted_bgw_log AS
    SELECT msg_no,
    	   mock_time,
    	   application_name,
    	   regexp_replace(regexp_replace(msg, '(Wait until|started at|execution time) [0-9]+(\.[0-9]+)?', '\1 (RANDOM)', 'g'), 'background worker "[^"]+"','connection') AS msg
	   FROM bgw_log ORDER BY mock_time, application_name COLLATE "C", msg_no;
CREATE TABLE public.bgw_dsm_handle_store(
    handle BIGINT
);
INSERT INTO public.bgw_dsm_handle_store VALUES (0);
SELECT ts_bgw_params_create();
 ts_bgw_params_create 
----------------------
 

SELECT * FROM _timescaledb_catalog.bgw_job;
 id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | fixed_schedule | initial_start | hypertable_id | config | check_schema | check_name | timezone 
----+------------------+-------------------+-------------+-------------+--------------+-------------+-----------+-------+-----------+----------------+---------------+---------------+--------+--------------+------------+----------

SELECT * FROM timescaledb_information.job_stats;
 hypertable_schema | hypertable_name | job_id | last_run_started_at | last_successful_finish | last_run_status | job_status | last_run_duration | next_start | total_runs | total_successes | total_failures 
-------------------+-----------------+--------+---------------------+------------------------+-----------------+------------+-------------------+------------+------------+-----------------+----------------

SELECT * FROM _timescaledb_catalog.continuous_agg;
 mat_hypertable_id | raw_hypertable_id | parent_mat_hypertable_id | user_view_schema | user_view_name | partial_view_schema | partial_view_name | direct_view_schema | direct_view_name | materialized_only 
-------------------+-------------------+--------------------------+------------------+----------------+---------------------+-------------------+--------------------+------------------+-------------------

-- though user on access node has required GRANTS, this will propagate GRANTS to the connected data nodes
GRANT CREATE ON SCHEMA public TO :ROLE_DEFAULT_PERM_USER;
WARNING:  no privileges were granted for "public"
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE test_continuous_agg_table(time int, data int);
SELECT create_hypertable('test_continuous_agg_table', 'time', chunk_time_interval => 10);
           create_hypertable            
----------------------------------------
 (1,public,test_continuous_agg_table,t)

CREATE OR REPLACE FUNCTION integer_now_test() returns int LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), 0) FROM test_continuous_agg_table $$;
SELECT set_integer_now_func('test_continuous_agg_table', 'integer_now_test');
 set_integer_now_func 
----------------------
 

CREATE MATERIALIZED VIEW test_continuous_agg_view
    WITH (timescaledb.continuous, timescaledb.materialized_only=true)
    AS SELECT time_bucket('2', time), SUM(data) as value
        FROM test_continuous_agg_table
        GROUP BY 1 WITH NO DATA;
SELECT add_continuous_aggregate_policy('test_continuous_agg_view', NULL, 4::integer, '12 h'::interval);
 add_continuous_aggregate_policy 
---------------------------------
                            1000

SELECT id as raw_table_id FROM _timescaledb_catalog.hypertable WHERE table_name='test_continuous_agg_table' \gset
-- min distance from end should be 1
SELECT mat_hypertable_id, user_view_schema, user_view_name FROM _timescaledb_catalog.continuous_agg;
 mat_hypertable_id | user_view_schema |      user_view_name      
-------------------+------------------+--------------------------
                 2 | public           | test_continuous_agg_view

SELECT mat_hypertable_id, bucket_width FROM _timescaledb_catalog.continuous_aggs_bucket_function;
 mat_hypertable_id | bucket_width 
-------------------+--------------
                 2 | 2

SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg \gset
SELECT id AS job_id FROM _timescaledb_catalog.bgw_job where hypertable_id=:mat_hypertable_id \gset
-- job was created
SELECT * FROM _timescaledb_catalog.bgw_job where hypertable_id=:mat_hypertable_id;
  id  |              application_name              | schedule_interval | max_runtime | max_retries | retry_period |      proc_schema       |              proc_name              |       owner       | scheduled | fixed_schedule | initial_start | hypertable_id |                             config                              |      check_schema      |                check_name                 | timezone 
------+--------------------------------------------+-------------------+-------------+-------------+--------------+------------------------+-------------------------------------+-------------------+-----------+----------------+---------------+---------------+-----------------------------------------------------------------+------------------------+-------------------------------------------+----------
 1000 | Refresh Continuous Aggregate Policy [1000] | @ 12 hours        | @ 0         |          -1 | @ 12 hours   | _timescaledb_functions | policy_refresh_continuous_aggregate | default_perm_user | t         | f              |               |             2 | {"end_offset": 4, "start_offset": null, "mat_hypertable_id": 2} | _timescaledb_functions | policy_refresh_continuous_aggregate_check | 

-- create 10 time buckets
INSERT INTO test_continuous_agg_table
    SELECT i, i FROM
        (SELECT generate_series(0, 10) as i) AS j;
-- no stats
SELECT job_id, next_start, last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
    FROM _timescaledb_internal.bgw_job_stat
    ORDER BY job_id;
 job_id | next_start | until_next | last_run_success | total_runs | total_successes | total_failures | total_crashes 
--------+------------+------------+------------------+------------+-----------------+----------------+---------------

-- no data in view
SELECT * FROM test_continuous_agg_view ORDER BY 1;
 time_bucket | value 
-------------+-------

-- run first time
SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25);
 ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish 
------------------------------------------------------------
 

SELECT * FROM sorted_bgw_log;
 msg_no | mock_time |              application_name              |                                                        msg                                                        
--------+-----------+--------------------------------------------+-------------------------------------------------------------------------------------------------------------------
      0 |         0 | DB Scheduler                               | [TESTING] Registered new background worker
      1 |         0 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 |         0 | Refresh Continuous Aggregate Policy [1000] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ -2147483648, 6 ]
      1 |         0 | Refresh Continuous Aggregate Policy [1000] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_2"
      2 |         0 | Refresh Continuous Aggregate Policy [1000] | inserted 3 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_2"

SELECT * FROM _timescaledb_catalog.bgw_job where id=:job_id;
  id  |              application_name              | schedule_interval | max_runtime | max_retries | retry_period |      proc_schema       |              proc_name              |       owner       | scheduled | fixed_schedule | initial_start | hypertable_id |                             config                              |      check_schema      |                check_name                 | timezone 
------+--------------------------------------------+-------------------+-------------+-------------+--------------+------------------------+-------------------------------------+-------------------+-----------+----------------+---------------+---------------+-----------------------------------------------------------------+------------------------+-------------------------------------------+----------
 1000 | Refresh Continuous Aggregate Policy [1000] | @ 12 hours        | @ 0         |          -1 | @ 12 hours   | _timescaledb_functions | policy_refresh_continuous_aggregate | default_perm_user | t         | f              |               |             2 | {"end_offset": 4, "start_offset": null, "mat_hypertable_id": 2} | _timescaledb_functions | policy_refresh_continuous_aggregate_check | 

-- job ran once, successfully
SELECT job_id, next_start-last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
    FROM _timescaledb_internal.bgw_job_stat
    where job_id=:job_id;
 job_id | until_next | last_run_success | total_runs | total_successes | total_failures | total_crashes 
--------+------------+------------------+------------+-----------------+----------------+---------------
   1000 | @ 12 hours | t                |          1 |               1 |              0 |             0

--clear log for next run of scheduler.
TRUNCATE public.bgw_log;
CREATE FUNCTION wait_for_timer_to_run(started_at INTEGER, spins INTEGER=:TEST_SPINWAIT_ITERS) RETURNS BOOLEAN LANGUAGE PLPGSQL AS
$BODY$
DECLARE
    num_runs INTEGER;
    message TEXT;
BEGIN
    select format('[TESTING] Wait until %%, started at %s', started_at) into message;
    FOR i in 1..spins
    LOOP
    SELECT COUNT(*) from bgw_log where msg LIKE message INTO num_runs;
    if (num_runs > 0) THEN
        RETURN true;
    ELSE
        PERFORM pg_sleep(0.1);
    END IF;
    END LOOP;
    RETURN false;
END
$BODY$;
--make sure there is 1 job to start with
SELECT test.wait_for_job_to_run(:job_id, 1);
 wait_for_job_to_run 
---------------------
 t

SELECT ts_bgw_params_mock_wait_returns_immediately(:WAIT_FOR_OTHER_TO_ADVANCE);
 ts_bgw_params_mock_wait_returns_immediately 
---------------------------------------------
 

--start the scheduler on 0 time
SELECT ts_bgw_params_reset_time(0, true);
 ts_bgw_params_reset_time 
--------------------------
 

SELECT ts_bgw_db_scheduler_test_run(extract(epoch from interval '24 hour')::int * 1000, 0);
 ts_bgw_db_scheduler_test_run 
------------------------------
 

SELECT wait_for_timer_to_run(0);
 wait_for_timer_to_run 
-----------------------
 t

--advance to 12:00 so that it runs one more time; now we know the
--scheduler has loaded up the job with the old schedule_interval
SELECT ts_bgw_params_reset_time(extract(epoch from interval '12 hour')::bigint * 1000000, true);
 ts_bgw_params_reset_time 
--------------------------
 

SELECT test.wait_for_job_to_run(:job_id, 2);
 wait_for_job_to_run 
---------------------
 t

--advance clock 1us to make the scheduler realize the job is done
SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint * 1000000)+1, true);
 ts_bgw_params_reset_time 
--------------------------
 

--alter the refresh interval and check if next_start is altered
SELECT alter_job(:job_id, schedule_interval => '1m', retry_period => '1m');
                                                                                                                           alter_job                                                                                                                            
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:01:00 2000 PST",_timescaledb_functions.policy_refresh_continuous_aggregate_check,f,,,"Refresh Continuous Aggregate Policy [1000]")

SELECT job_id, next_start - last_finish as until_next, total_runs
FROM _timescaledb_internal.bgw_job_stat
WHERE job_id=:job_id;;
 job_id | until_next | total_runs 
--------+------------+------------
   1000 | @ 1 min    |          2

--advance to 12:02, job should have run at 12:01
SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint * 1000000)+(extract(epoch from interval '2 minute')::bigint * 1000000), true);
 ts_bgw_params_reset_time 
--------------------------
 

SELECT test.wait_for_job_to_run(:job_id, 3);
 wait_for_job_to_run 
---------------------
 t

--next run in 1 minute
SELECT job_id, next_start-last_finish as until_next, total_runs
FROM _timescaledb_internal.bgw_job_stat
WHERE job_id=:job_id;
 job_id | until_next | total_runs 
--------+------------+------------
   1000 | @ 1 min    |          3

--change next run to be after 30s instead
SELECT (next_start - '30s'::interval) AS "NEW_NEXT_START"
FROM _timescaledb_internal.bgw_job_stat
WHERE job_id=:job_id \gset
SELECT alter_job(:job_id, next_start => :'NEW_NEXT_START');
                                                                                                                           alter_job                                                                                                                            
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:02:30 2000 PST",_timescaledb_functions.policy_refresh_continuous_aggregate_check,f,,,"Refresh Continuous Aggregate Policy [1000]")

SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint * 1000000)+(extract(epoch from interval '2 minute 30 seconds')::bigint * 1000000), true);
 ts_bgw_params_reset_time 
--------------------------
 

SELECT test.wait_for_job_to_run(:job_id, 4);
 wait_for_job_to_run 
---------------------
 t

--advance clock to quit scheduler
SELECT ts_bgw_params_reset_time(extract(epoch from interval '25 hour')::bigint * 1000000, true);
 ts_bgw_params_reset_time 
--------------------------
 

select ts_bgw_db_scheduler_test_wait_for_scheduler_finish();
 ts_bgw_db_scheduler_test_wait_for_scheduler_finish 
----------------------------------------------------
 

SELECT ts_bgw_params_mock_wait_returns_immediately(:WAIT_ON_JOB);
 ts_bgw_params_mock_wait_returns_immediately 
---------------------------------------------
 

TRUNCATE public.bgw_log;
-- data before 8
SELECT * FROM test_continuous_agg_view ORDER BY 1;
 time_bucket | value 
-------------+-------
           0 |     1
           2 |     5
           4 |     9

-- invalidations test by running job multiple times
SELECT ts_bgw_params_reset_time();
 ts_bgw_params_reset_time 
--------------------------
 

DROP MATERIALIZED VIEW test_continuous_agg_view;
NOTICE:  drop cascades to table _timescaledb_internal._hyper_2_3_chunk
CREATE MATERIALIZED VIEW test_continuous_agg_view
    WITH (timescaledb.continuous,
        timescaledb.materialized_only=true)
    AS SELECT time_bucket('2', time), SUM(data) as value
        FROM test_continuous_agg_table
        GROUP BY 1 WITH NO DATA;
SELECT add_continuous_aggregate_policy('test_continuous_agg_view', 100::integer, -2::integer, '12 h'::interval);
 add_continuous_aggregate_policy 
---------------------------------
                            1001

SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg \gset
SELECT id AS job_id FROM _timescaledb_catalog.bgw_job WHERE hypertable_id=:mat_hypertable_id \gset
SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25);
 ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish 
------------------------------------------------------------
 

SELECT * FROM sorted_bgw_log;
 msg_no | mock_time |              application_name              |                                                            msg                                                            
--------+-----------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------------------
      0 |         0 | DB Scheduler                               | [TESTING] Registered new background worker
      1 |         0 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 |         0 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ 10, 12 ] (batch 1 of 2)
      1 |         0 | Refresh Continuous Aggregate Policy [1001] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      2 |         0 | Refresh Continuous Aggregate Policy [1001] | inserted 1 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"
      3 |         0 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ -10, 10 ] (batch 2 of 2)
      4 |         0 | Refresh Continuous Aggregate Policy [1001] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      5 |         0 | Refresh Continuous Aggregate Policy [1001] | inserted 5 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"

-- job ran once, successfully
SELECT job_id, last_finish - next_start as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
    FROM _timescaledb_internal.bgw_job_stat
    where job_id=:job_id;
 job_id |   until_next   | last_run_success | total_runs | total_successes | total_failures | total_crashes 
--------+----------------+------------------+------------+-----------------+----------------+---------------
   1001 | @ 12 hours ago | t                |          1 |               1 |              0 |             0

-- should have refreshed everything we have so far
SELECT * FROM test_continuous_agg_view ORDER BY 1;
 time_bucket | value 
-------------+-------
           0 |     1
           2 |     5
           4 |     9
           6 |    13
           8 |    17
          10 |    10

-- invalidate some data
UPDATE test_continuous_agg_table
SET data = 11 WHERE time = 6;
--advance time by 12h so that job runs one more time
SELECT ts_bgw_params_reset_time(extract(epoch from interval '12 hour')::bigint * 1000000, true);
 ts_bgw_params_reset_time 
--------------------------
 

SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25, 25);
 ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish 
------------------------------------------------------------
 

SELECT * FROM sorted_bgw_log;
 msg_no |  mock_time  |              application_name              |                                                            msg                                                            
--------+-------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------------------
      0 |           0 | DB Scheduler                               | [TESTING] Registered new background worker
      1 |           0 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 |           0 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ 10, 12 ] (batch 1 of 2)
      1 |           0 | Refresh Continuous Aggregate Policy [1001] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      2 |           0 | Refresh Continuous Aggregate Policy [1001] | inserted 1 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"
      3 |           0 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ -10, 10 ] (batch 2 of 2)
      4 |           0 | Refresh Continuous Aggregate Policy [1001] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      5 |           0 | Refresh Continuous Aggregate Policy [1001] | inserted 5 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"
      0 | 43200000000 | DB Scheduler                               | [TESTING] Registered new background worker
      1 | 43200000000 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ -90, -10 ]
      1 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      2 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | inserted 0 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"
      3 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ 6, 8 ]
      4 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | deleted 1 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      5 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | inserted 1 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"

SELECT job_id, next_start - last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
    FROM _timescaledb_internal.bgw_job_stat
    where job_id=:job_id;
 job_id | until_next | last_run_success | total_runs | total_successes | total_failures | total_crashes 
--------+------------+------------------+------------+-----------------+----------------+---------------
   1001 | @ 12 hours | t                |          2 |               2 |              0 |             0

-- should have updated data for time=6
SELECT * FROM test_continuous_agg_view ORDER BY 1;
 time_bucket | value 
-------------+-------
           0 |     1
           2 |     5
           4 |     9
           6 |    18
           8 |    17
          10 |    10

\x on
--check the information views --
select view_name, view_owner, materialization_hypertable_schema, materialization_hypertable_name
from timescaledb_information.continuous_aggregates
where view_name::text like '%test_continuous_agg_view';
-[ RECORD 1 ]---------------------+---------------------------
view_name                         | test_continuous_agg_view
view_owner                        | default_perm_user
materialization_hypertable_schema | _timescaledb_internal
materialization_hypertable_name   | _materialized_hypertable_3

select view_name, view_definition from timescaledb_information.continuous_aggregates
where view_name::text like '%test_continuous_agg_view';
-[ RECORD 1 ]---+-----------------------------------------------
view_name       | test_continuous_agg_view
view_definition |  SELECT time_bucket(2, "time") AS time_bucket,+
                |     sum(data) AS value                        +
                |    FROM test_continuous_agg_table             +
                |   GROUP BY (time_bucket(2, "time"));

select job_status, last_run_duration
from timescaledb_information.job_stats ps, timescaledb_information.continuous_aggregates cagg
where cagg.view_name::text like '%test_continuous_agg_view'
and cagg.materialization_hypertable_name = ps.hypertable_name;
-[ RECORD 1 ]-----+----------
job_status        | Scheduled
last_run_duration | 

\x off
-- test merged refresh (change data in two chunks)
UPDATE test_continuous_agg_table SET data = 11;
--advance time by 1day so that job runs one more time
SELECT ts_bgw_params_reset_time(extract(epoch from interval '1day')::bigint * 1000000, true);
 ts_bgw_params_reset_time 
--------------------------
 

SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(50, 50);
 ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish 
------------------------------------------------------------
 

SELECT * FROM sorted_bgw_log;
 msg_no |  mock_time  |              application_name              |                                                            msg                                                            
--------+-------------+--------------------------------------------+---------------------------------------------------------------------------------------------------------------------------
      0 |           0 | DB Scheduler                               | [TESTING] Registered new background worker
      1 |           0 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 |           0 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ 10, 12 ] (batch 1 of 2)
      1 |           0 | Refresh Continuous Aggregate Policy [1001] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      2 |           0 | Refresh Continuous Aggregate Policy [1001] | inserted 1 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"
      3 |           0 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ -10, 10 ] (batch 2 of 2)
      4 |           0 | Refresh Continuous Aggregate Policy [1001] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      5 |           0 | Refresh Continuous Aggregate Policy [1001] | inserted 5 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"
      0 | 43200000000 | DB Scheduler                               | [TESTING] Registered new background worker
      1 | 43200000000 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ -90, -10 ]
      1 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      2 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | inserted 0 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"
      3 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ 6, 8 ]
      4 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | deleted 1 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      5 | 43200000000 | Refresh Continuous Aggregate Policy [1001] | inserted 1 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"
      0 | 86400000000 | DB Scheduler                               | [TESTING] Registered new background worker
      1 | 86400000000 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 | 86400000000 | Refresh Continuous Aggregate Policy [1001] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view" in window [ 0, 12 ]
      1 | 86400000000 | Refresh Continuous Aggregate Policy [1001] | deleted 6 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_3"
      2 | 86400000000 | Refresh Continuous Aggregate Policy [1001] | inserted 6 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_3"

DROP MATERIALIZED VIEW test_continuous_agg_view;
NOTICE:  drop cascades to table _timescaledb_internal._hyper_3_4_chunk
--create a view with a function that it has no permission to execute
CREATE MATERIALIZED VIEW test_continuous_agg_view
    WITH (timescaledb.continuous,
        timescaledb.materialized_only=true)
    AS SELECT time_bucket('2', time), SUM(data) as value, get_constant_no_perms()
        FROM test_continuous_agg_table
        GROUP BY 1 WITH NO DATA;
SELECT add_continuous_aggregate_policy('test_continuous_agg_view', 100::integer, -2::integer, '12 h'::interval);
 add_continuous_aggregate_policy 
---------------------------------
                            1002

SELECT id AS job_id FROM _timescaledb_catalog.bgw_job ORDER BY id desc limit 1 \gset
SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25);
 ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish 
------------------------------------------------------------
 

-- job fails
SELECT job_id, last_run_success, total_runs, total_successes, total_failures, total_crashes
    FROM _timescaledb_internal.bgw_job_stat
    where job_id=:job_id;
 job_id | last_run_success | total_runs | total_successes | total_failures | total_crashes 
--------+------------------+------------+-----------------+----------------+---------------
   1002 | f                |          1 |               0 |              1 |             0

DROP MATERIALIZED VIEW test_continuous_agg_view;
--advance clock to quit scheduler
SELECT ts_bgw_params_reset_time(extract(epoch from interval '25 hour')::bigint * 1000000, true);
 ts_bgw_params_reset_time 
--------------------------
 

select ts_bgw_db_scheduler_test_wait_for_scheduler_finish();
 ts_bgw_db_scheduler_test_wait_for_scheduler_finish 
----------------------------------------------------
 

SELECT ts_bgw_params_mock_wait_returns_immediately(:WAIT_ON_JOB);
 ts_bgw_params_mock_wait_returns_immediately 
---------------------------------------------
 

--clear log for next run of the scheduler
TRUNCATE public.bgw_log;
SELECT ts_bgw_params_reset_time();
 ts_bgw_params_reset_time 
--------------------------
 

--
-- Test creating continuous aggregate with a user that is the non-owner of the raw table
--
CREATE TABLE test_continuous_agg_table_w_grant(time int, data int);
SELECT create_hypertable('test_continuous_agg_table_w_grant', 'time', chunk_time_interval => 10);
               create_hypertable                
------------------------------------------------
 (5,public,test_continuous_agg_table_w_grant,t)

CREATE OR REPLACE FUNCTION integer_now_test1() returns int LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), 0) FROM test_continuous_agg_table_w_grant $$;
SELECT set_integer_now_func('test_continuous_agg_table_w_grant', 'integer_now_test1');
 set_integer_now_func 
----------------------
 

GRANT SELECT, TRIGGER ON test_continuous_agg_table_w_grant TO public;
INSERT INTO test_continuous_agg_table_w_grant
    SELECT 1 , 1;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
-- make sure view can be created
CREATE MATERIALIZED VIEW test_continuous_agg_view_user_2
    WITH ( timescaledb.continuous,
        timescaledb.materialized_only=true)
    AS SELECT time_bucket('2', time), SUM(data) as value
        FROM test_continuous_agg_table_w_grant
        GROUP BY 1 WITH NO DATA;
SELECT add_continuous_aggregate_policy('test_continuous_agg_view_user_2', NULL, -2::integer, '12 h'::interval);
 add_continuous_aggregate_policy 
---------------------------------
                            1003

SELECT id AS job_id FROM _timescaledb_catalog.bgw_job ORDER BY id desc limit 1 \gset
SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25);
 ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish 
------------------------------------------------------------
 

SELECT id, owner FROM _timescaledb_catalog.bgw_job WHERE id = :job_id ;
  id  |        owner        
------+---------------------
 1003 | default_perm_user_2

SELECT job_id, next_start - last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
    FROM _timescaledb_internal.bgw_job_stat
    where job_id=:job_id;
 job_id | until_next | last_run_success | total_runs | total_successes | total_failures | total_crashes 
--------+------------+------------------+------------+-----------------+----------------+---------------
   1003 | @ 12 hours | t                |          1 |               1 |              0 |             0

--view is populated
SELECT * FROM test_continuous_agg_view_user_2 ORDER BY 1;
 time_bucket | value 
-------------+-------
           0 |     1

\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
--revoke permissions from the continuous agg view owner to select from raw table
--no further updates to cont agg should happen
REVOKE SELECT ON test_continuous_agg_table_w_grant FROM public;
--add new data to table
INSERT INTO test_continuous_agg_table_w_grant VALUES(5,1);
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
--advance time by 12h so that job tries to run one more time
SELECT ts_bgw_params_reset_time(extract(epoch from interval '12 hour')::bigint * 1000000, true);
 ts_bgw_params_reset_time 
--------------------------
 

SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25, 25);
 ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish 
------------------------------------------------------------
 

--should show a failing execution because no longer has permissions (due to lack of permission on partial view owner's part)
SELECT job_id, last_run_success, total_runs, total_successes, total_failures, total_crashes
    FROM _timescaledb_internal.bgw_job_stat
    where job_id=:job_id;
 job_id | last_run_success | total_runs | total_successes | total_failures | total_crashes 
--------+------------------+------------+-----------------+----------------+---------------
   1003 | f                |          2 |               1 |              1 |             0

--view was NOT updated; but the old stuff is still there
SELECT * FROM test_continuous_agg_view_user_2;
 time_bucket | value 
-------------+-------
           0 |     1

\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
SELECT * from sorted_bgw_log;
 msg_no |  mock_time  |              application_name              |                                                           msg                                                            
--------+-------------+--------------------------------------------+--------------------------------------------------------------------------------------------------------------------------
      0 |           0 | DB Scheduler                               | [TESTING] Registered new background worker
      1 |           0 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 |           0 | Refresh Continuous Aggregate Policy [1003] | continuous aggregate refresh (individual invalidation) on "test_continuous_agg_view_user_2" in window [ -2147483648, 2 ]
      1 |           0 | Refresh Continuous Aggregate Policy [1003] | deleted 0 row(s) from materialization table "_timescaledb_internal._materialized_hypertable_6"
      2 |           0 | Refresh Continuous Aggregate Policy [1003] | inserted 1 row(s) into materialization table "_timescaledb_internal._materialized_hypertable_6"
      0 | 43200000000 | DB Scheduler                               | [TESTING] Registered new background worker
      1 | 43200000000 | DB Scheduler                               | [TESTING] Wait until (RANDOM), started at (RANDOM)
      0 | 43200000000 | Refresh Continuous Aggregate Policy [1003] | job 1003 threw an error
      1 | 43200000000 | Refresh Continuous Aggregate Policy [1003] | permission denied for table test_continuous_agg_table_w_grant

-- Count the number of continuous aggregate policies
SELECT count(*) FROM _timescaledb_catalog.bgw_job
 WHERE proc_schema = '_timescaledb_functions'
   AND proc_name = 'policy_refresh_continuous_aggregate';
 count 
-------
     1

