-- This file and its contents are licensed under the Timescale License. -- Please see the included NOTICE for copyright information and -- LICENSE-TIMESCALE for a copy of the license. \c :TEST_DBNAME :ROLE_SUPERUSER CREATE OR REPLACE FUNCTION run_continuous_agg_materialization( hypertable REGCLASS, materialization_table REGCLASS, input_view NAME, lowest_modified_value ANYELEMENT, greatest_modified_value ANYELEMENT, bucket_width "any", input_view_schema NAME = 'public' ) RETURNS VOID AS :TSL_MODULE_PATHNAME, 'ts_run_continuous_agg_materialization' LANGUAGE C VOLATILE; SET ROLE :ROLE_DEFAULT_PERM_USER; CREATE TABLE continuous_agg_test(time BIGINT, data BIGINT, dummy BOOLEAN); select create_hypertable('continuous_agg_test', 'time', chunk_time_interval=> 10); -- simulated materialization table CREATE TABLE materialization(time_bucket BIGINT, value BIGINT); select create_hypertable('materialization', 'time_bucket', chunk_time_interval => 100); SET ROLE :ROLE_SUPERUSER; INSERT INTO _timescaledb_catalog.continuous_aggs_invalidation_threshold VALUES (1, 14); SET ROLE :ROLE_DEFAULT_PERM_USER; -- simulated continuous_agg insert view CREATE VIEW test_view(time_bucket, value) AS SELECT time_bucket(2, time), COUNT(data) as value FROM continuous_agg_test GROUP BY 1; INSERT INTO continuous_agg_test VALUES (10, 1), (11, 2), (12, 3), (13, 4), (14, 1), (15, 1), (16, 1), (17, 1); SELECT * FROM test_view ORDER BY 1; SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- materialize some of the data into the view SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 9, 12, 2); SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- materialize out of bounds is a nop SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 16, 19, 2); SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- materialize the rest of the data SET ROLE :ROLE_SUPERUSER; DELETE FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold WHERE hypertable_id = 1; INSERT INTO _timescaledb_catalog.continuous_aggs_invalidation_threshold VALUES (1, 16); SET ROLE :ROLE_DEFAULT_PERM_USER; SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 12, 17, 2); SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- insert some additional data into the materialization table -- (as if we had stale materializations) INSERT INTO materialization VALUES (11, 1), (13, 3), (15, 4); SELECT * FROM materialization ORDER BY 1; -- materializing should delete the invalid data, and leave the correct data SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 10, 15, 2); SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- test gaps INSERT INTO continuous_agg_test VALUES (9, 0), (10, 1), (11, 2), (12, 3), (13, 4), (20, 1), (21, 1); SET ROLE :ROLE_SUPERUSER; DELETE FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold WHERE hypertable_id = 1; INSERT INTO _timescaledb_catalog.continuous_aggs_invalidation_threshold VALUES (1, 22); SET ROLE :ROLE_DEFAULT_PERM_USER; SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 9, 9, 2); SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- fill in the remaining SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 10, 12, 2); SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- the bucket containing big_int_max should not be materialized, but all others should SELECT 9223372036854775807 as big_int_max \gset SELECT -9223372036854775808 as big_int_min \gset SET ROLE :ROLE_SUPERUSER; DELETE FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold WHERE hypertable_id = 1; INSERT INTO _timescaledb_catalog.continuous_aggs_invalidation_threshold VALUES (1, :big_int_max-1); SET ROLE :ROLE_DEFAULT_PERM_USER; INSERT INTO continuous_agg_test VALUES (:big_int_max - 4, 1), (:big_int_max - 3, 5), (:big_int_max - 2, 7), (:big_int_max - 1, 9), (:big_int_max, 11), (:big_int_min, 22), (:big_int_min + 1, 23); SET client_min_messages TO error; SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 10, 12, 2); RESET client_min_messages; SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- test invalidations SET client_min_messages TO error; SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', :big_int_max-6, :big_int_max, 2); RESET client_min_messages; SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; SET client_min_messages TO error; SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', :big_int_min, :big_int_max, 2); RESET client_min_messages; SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; TRUNCATE materialization; -- test dropping columns ALTER TABLE continuous_agg_test DROP COLUMN dummy; SET client_min_messages TO error; SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 9, 13, 2); RESET client_min_messages; SELECT * FROM materialization; ALTER TABLE continuous_agg_test ADD COLUMN d2 int; TRUNCATE materialization; SET client_min_messages TO error; SELECT run_continuous_agg_materialization('continuous_agg_test', 'materialization', 'test_view', 9, 13, 2); RESET client_min_messages; SELECT * FROM materialization; TRUNCATE materialization; -- name and view that needs quotes CREATE VIEW "view with spaces"(time_bucket, value) AS SELECT time_bucket(2, time), COUNT(data) as value FROM continuous_agg_test GROUP BY 1; CREATE TABLE "table with spaces"(time_bucket BIGINT, value BIGINT); select create_hypertable('"table with spaces"'::REGCLASS, 'time_bucket', chunk_time_interval => 100); SET client_min_messages TO error; SELECT run_continuous_agg_materialization('continuous_agg_test', '"table with spaces"', 'view with spaces', 9, 21, 2); RESET client_min_messages; SELECT * FROM "view with spaces" ORDER BY 1; SELECT * FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT * FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; DROP TABLE materialization; ---------------------------- ---------------------------- ---------------------------- -- test with a time type SET SESSION timezone TO 'UTC'; SET SESSION datestyle TO 'ISO'; CREATE TABLE materialization(time_bucket TIMESTAMPTZ, value BIGINT); select create_hypertable('materialization', 'time_bucket'); CREATE TABLE continuous_agg_test_t(time TIMESTAMPTZ, data int); select create_hypertable('continuous_agg_test_t', 'time'); INSERT INTO continuous_agg_test_t VALUES ('2019-02-02 2:00 UTC', 1), ('2019-02-02 3:00 UTC', 1), ('2019-02-02 4:00 UTC', 1), ('2019-02-02 5:00 UTC', 1); SELECT * FROM continuous_agg_test_t; TRUNCATE materialization; SET ROLE :ROLE_SUPERUSER; INSERT INTO _timescaledb_catalog.continuous_aggs_invalidation_threshold VALUES (5, _timescaledb_internal.time_to_internal('2019-02-02 4:00 UTC'::TIMESTAMPTZ)); TRUNCATE _timescaledb_catalog.continuous_aggs_completed_threshold; SET ROLE :ROLE_DEFAULT_PERM_USER; SET SESSION timezone TO 'UTC'; SET SESSION datestyle TO 'ISO'; CREATE VIEW test_view_t(time_bucket, value) AS SELECT time_bucket('2 hours', time) as time_bucket, COUNT(data) as value FROM continuous_agg_test_t GROUP BY 1; SELECT run_continuous_agg_materialization('continuous_agg_test_t', 'materialization', 'test_view_t'::NAME, '2019-02-02 2:00 UTC'::TIMESTAMPTZ, '2019-02-02 3:00 UTC'::TIMESTAMPTZ, '2 hours'::INTERVAL); SELECT time_bucket, value FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT materialization_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id = 4; SELECT run_continuous_agg_materialization('continuous_agg_test_t', 'materialization', 'test_view_t'::NAME, '2019-02-02 4:00 UTC'::TIMESTAMPTZ, '2019-02-02 5:00 UTC'::TIMESTAMPTZ, '2 hours'::INTERVAL); SELECT time_bucket, value FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT materialization_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id = 4; SET ROLE :ROLE_SUPERUSER; DELETE FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold WHERE hypertable_id = 5; INSERT INTO _timescaledb_catalog.continuous_aggs_invalidation_threshold VALUES (5, _timescaledb_internal.time_to_internal('2019-02-02 6:00 UTC'::TIMESTAMPTZ)); TRUNCATE _timescaledb_catalog.continuous_aggs_completed_threshold; SET ROLE :ROLE_DEFAULT_PERM_USER; SET SESSION timezone TO 'UTC'; SET SESSION datestyle TO 'ISO'; SELECT run_continuous_agg_materialization('continuous_agg_test_t', 'materialization', 'test_view_t'::NAME, '2019-02-02 4:00 UTC'::TIMESTAMPTZ, '2019-02-02 5:00 UTC'::TIMESTAMPTZ, '2 hours'::INTERVAL); SELECT time_bucket, value FROM materialization ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT materialization_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id = 4; ---------------------------- ---------------------------- ---------------------------- -- test with a real continuous aggregate SET ROLE :ROLE_SUPERUSER; TRUNCATE _timescaledb_catalog.continuous_aggs_completed_threshold; TRUNCATE _timescaledb_catalog.continuous_aggs_invalidation_threshold; SET ROLE :ROLE_DEFAULT_PERM_USER; SET SESSION timezone TO 'UTC'; SET SESSION datestyle TO 'ISO'; SET client_min_messages TO LOG; SELECT * FROM continuous_agg_test_t; CREATE VIEW test_t_mat_view WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS SELECT time_bucket('2 hours', time), COUNT(data) as value FROM continuous_agg_test_t GROUP BY 1; SELECT mat_hypertable_id, raw_hypertable_id, user_view_schema, user_view_name, partial_view_schema, partial_view_name, _timescaledb_internal.to_timestamp(bucket_width), _timescaledb_internal.to_interval(refresh_lag), job_id FROM _timescaledb_catalog.continuous_agg; SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg \gset SET ROLE :ROLE_SUPERUSER; UPDATE _timescaledb_catalog.continuous_agg SET refresh_lag=7200000000 WHERE mat_hypertable_id=:mat_hypertable_id; SET ROLE :ROLE_DEFAULT_PERM_USER; SET SESSION timezone TO 'UTC'; SET SESSION datestyle TO 'ISO'; SELECT _timescaledb_internal.to_interval(refresh_lag) FROM _timescaledb_catalog.continuous_agg; SELECT ca.raw_hypertable_id as "RAW_HYPERTABLE_ID", h.schema_name AS "MAT_SCHEMA_NAME", h.table_name AS "MAT_TABLE_NAME", partial_view_name as "PART_VIEW_NAME", partial_view_schema as "PART_VIEW_SCHEMA" FROM _timescaledb_catalog.continuous_agg ca INNER JOIN _timescaledb_catalog.hypertable h ON(h.id = ca.mat_hypertable_id) WHERE user_view_name = 'test_t_mat_view' \gset SELECT * FROM test_t_mat_view; SELECT * FROM :"MAT_SCHEMA_NAME".:"MAT_TABLE_NAME" ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT materialization_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id = :mat_hypertable_id; SET timescaledb.current_timestamp_mock = '2019-02-02 5:00 UTC'; REFRESH MATERIALIZED VIEW test_t_mat_view; SELECT * FROM :"MAT_SCHEMA_NAME".:"MAT_TABLE_NAME" ORDER BY 1; SELECT * FROM test_t_mat_view ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT materialization_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id = :mat_hypertable_id; SELECT hypertable_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; REFRESH MATERIALIZED VIEW test_t_mat_view; SELECT * FROM :"MAT_SCHEMA_NAME".:"MAT_TABLE_NAME" ORDER BY 1; SELECT * FROM test_t_mat_view ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT materialization_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id = :mat_hypertable_id; SELECT hypertable_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- increase the current timestamp, so we actually materialize SET timescaledb.current_timestamp_mock = '2019-02-02 7:00 UTC'; INSERT INTO continuous_agg_test_t VALUES ('2019-02-02 7:00 UTC', 1); SELECT * FROM continuous_agg_test_t ORDER BY 1; REFRESH MATERIALIZED VIEW test_t_mat_view; SELECT * FROM test_t_mat_view ORDER BY 1; SELECT materialization_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id = :mat_hypertable_id; SELECT hypertable_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- test invalidations INSERT INTO continuous_agg_test_t VALUES ('2019-02-02 2:00 UTC', 1), ('2019-02-02 3:00 UTC', 1), ('2019-02-02 4:00 UTC', 1), ('2019-02-02 5:00 UTC', 1); SELECT * FROM _timescaledb_catalog.continuous_aggs_hypertable_invalidation_log; REFRESH MATERIALIZED VIEW test_t_mat_view; SELECT * FROM test_t_mat_view ORDER BY 1; SELECT * FROM _timescaledb_catalog.continuous_aggs_completed_threshold; SELECT materialization_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id = :mat_hypertable_id; SELECT hypertable_id, _timescaledb_internal.to_timestamp(watermark) FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold; -- test extremes CREATE TABLE continuous_agg_extreme(time BIGINT, data BIGINT); SELECT create_hypertable('continuous_agg_extreme', 'time', chunk_time_interval=> 10); CREATE OR REPLACE FUNCTION integer_now_continuous_agg_extreme() returns BIGINT LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), BIGINT '-9223372036854775808') FROM continuous_agg_extreme $$; SELECT set_integer_now_func('continuous_agg_extreme', 'integer_now_continuous_agg_extreme'); -- We should be able to use time_bucket(5, ...) (note lack of '), but that is currently not -- recognized as a constant CREATE VIEW extreme_view WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS SELECT time_bucket('1', time), SUM(data) as value FROM continuous_agg_extreme GROUP BY 1; SELECT id as raw_table_id FROM _timescaledb_catalog.hypertable WHERE table_name='continuous_agg_extreme' \gset SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg WHERE raw_hypertable_id=:raw_table_id \gset -- EMPTY table should be a nop REFRESH MATERIALIZED VIEW extreme_view; SELECT * FROM extreme_view; SELECT materialization_id, watermark FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id=:mat_hypertable_id; SELECT hypertable_id, watermark FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold WHERE hypertable_id=:raw_table_id; -- less than a bucket above MIN should be a nop INSERT INTO continuous_agg_extreme VALUES (:big_int_min, 1); REFRESH MATERIALIZED VIEW extreme_view; SELECT * FROM extreme_view; SELECT materialization_id, watermark FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id=:mat_hypertable_id; SELECT hypertable_id, watermark FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold WHERE hypertable_id=:raw_table_id; -- but we will be able to materialize it once we have enough values INSERT INTO continuous_agg_extreme VALUES (:big_int_min+10, 11); REFRESH MATERIALIZED VIEW extreme_view; SELECT * FROM extreme_view; SELECT materialization_id, watermark FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id=:mat_hypertable_id; SELECT hypertable_id, watermark FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold WHERE hypertable_id=:raw_table_id; -- we don't materialize the max value, but attempting will materialize the entire -- table up to the materialization limit INSERT INTO continuous_agg_extreme VALUES (100, 101), (:big_int_max-5, 201), (:big_int_max-4, 201), (:big_int_max-3, 201), (:big_int_max-2, 201), (:big_int_max-1, 201), (:big_int_max, :big_int_max); REFRESH MATERIALIZED VIEW extreme_view; SELECT * FROM extreme_view ORDER BY 1; SELECT materialization_id, watermark FROM _timescaledb_catalog.continuous_aggs_completed_threshold WHERE materialization_id=:mat_hypertable_id; SELECT hypertable_id, watermark FROM _timescaledb_catalog.continuous_aggs_invalidation_threshold WHERE hypertable_id=:raw_table_id; --cleanup of continuous agg views -- DROP view test_t_mat_view CASCADE; DROP view extreme_view CASCADE; -- negative lag test CREATE TABLE continuous_agg_negative(time BIGINT, data BIGINT); SELECT create_hypertable('continuous_agg_negative', 'time', chunk_time_interval=> 10); CREATE OR REPLACE FUNCTION integer_now_continuous_agg_negative() returns BIGINT LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), BIGINT '-9223372036854775808') FROM continuous_agg_negative $$; SELECT set_integer_now_func('continuous_agg_negative', 'integer_now_continuous_agg_negative'); CREATE VIEW negative_view_5 WITH (timescaledb.continuous, timescaledb.materialized_only=true, timescaledb.refresh_lag='-2') AS SELECT time_bucket('5', time), COUNT(data) as value FROM continuous_agg_negative GROUP BY 1; -- two chunks, 4 buckets INSERT INTO continuous_agg_negative SELECT i, i FROM generate_series(0, 11) AS i; REFRESH MATERIALIZED VIEW negative_view_5; SELECT * FROM negative_view_5 ORDER BY 1; -- inserting 12 and 13 will cause the next bucket to materialize -- even though the time_bucket would require us INSERT 14 INSERT INTO continuous_agg_negative VALUES (12, 12), (13, 13); REFRESH MATERIALIZED VIEW negative_view_5; SELECT * FROM negative_view_5 ORDER BY 1; -- bucket is finished as normal with the additional value INSERT INTO continuous_agg_negative VALUES (14, 14); REFRESH MATERIALIZED VIEW negative_view_5; SELECT * FROM negative_view_5 ORDER BY 1; --inserting a really large value does not work because of max_interval_per_job INSERT INTO continuous_agg_negative VALUES (:big_int_max-3, 201); REFRESH MATERIALIZED VIEW negative_view_5; SELECT * FROM negative_view_5 ORDER BY 1; DROP VIEW negative_view_5 CASCADE; TRUNCATE continuous_agg_negative; CREATE VIEW negative_view_5 WITH (timescaledb.continuous, timescaledb.materialized_only=true, timescaledb.refresh_lag='-2') AS SELECT time_bucket('5', time), COUNT(data) as value FROM continuous_agg_negative GROUP BY 1; -- we can handle values near max INSERT INTO continuous_agg_negative VALUES (:big_int_max-3, 201); SET client_min_messages TO error; REFRESH MATERIALIZED VIEW negative_view_5; RESET client_min_messages; SELECT * FROM negative_view_5 ORDER BY 1; -- even when the subtraction would make a completion time greater than max INSERT INTO continuous_agg_negative VALUES (:big_int_max-1, 201); SET client_min_messages TO error; REFRESH MATERIALIZED VIEW negative_view_5; RESET client_min_messages; SELECT * FROM negative_view_5 ORDER BY 1; DROP TABLE continuous_agg_negative CASCADE; -- max_interval_per_job tests CREATE TABLE continuous_agg_max_mat(time BIGINT, data BIGINT); SELECT create_hypertable('continuous_agg_max_mat', 'time', chunk_time_interval=> 10); CREATE OR REPLACE FUNCTION integer_now_continuous_agg_max_mat() returns BIGINT LANGUAGE SQL STABLE as $$ SELECT coalesce(max(time), BIGINT '-9223372036854775808') FROM continuous_agg_max_mat $$; SELECT set_integer_now_func('continuous_agg_max_mat', 'integer_now_continuous_agg_max_mat'); -- only allow two time_buckets per run CREATE VIEW max_mat_view WITH (timescaledb.continuous, timescaledb.materialized_only=true, timescaledb.max_interval_per_job='4', timescaledb.refresh_lag='-2') AS SELECT time_bucket('2', time), COUNT(data) as value FROM continuous_agg_max_mat GROUP BY 1; INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(0, 10) AS i; SET client_min_messages TO LOG; -- first run create two materializations REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; -- repeated runs will eventually materialize all the data REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; --one invalidation covered by max_interval refreshed right away INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(0, 3) AS i; REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; --nothing to do REFRESH MATERIALIZED VIEW max_mat_view; --one invalidation too big for max_interval will be split INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(0, 6) AS i; REFRESH MATERIALIZED VIEW max_mat_view; REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; --nothing to do REFRESH MATERIALIZED VIEW max_mat_view; --many invalidation too big for max_interval will be split INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(0, 1) AS i; INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(2, 3) AS i; INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(4, 5) AS i; REFRESH MATERIALIZED VIEW max_mat_view; REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; --nothing to do REFRESH MATERIALIZED VIEW max_mat_view; --one invalidation + new entries requires two refreshes if the refresh budget taken up by invalidation INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(0, 3) AS i; INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(11, 12) AS i; REFRESH MATERIALIZED VIEW max_mat_view; REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; --nothing to do REFRESH MATERIALIZED VIEW max_mat_view; --one invalidation + new entries done in one refresh if the refresh budget allows INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(0, 1) AS i; INSERT INTO continuous_agg_max_mat SELECT i, i FROM generate_series(14, 15) AS i; REFRESH MATERIALIZED VIEW max_mat_view; SELECT * FROM max_mat_view ORDER BY 1; --nothing to do. REFRESH MATERIALIZED VIEW max_mat_view; -- time type CREATE TABLE continuous_agg_max_mat_t(time TIMESTAMPTZ, data TIMESTAMPTZ); SELECT create_hypertable('continuous_agg_max_mat_t', 'time'); -- only allow two time_buckets per run CREATE VIEW max_mat_view_t WITH (timescaledb.continuous, timescaledb.materialized_only=true, timescaledb.max_interval_per_job='4 hours', timescaledb.refresh_lag='-2 hours') AS SELECT time_bucket('2 hours', time), COUNT(data) as value FROM continuous_agg_max_mat_t GROUP BY 1; INSERT INTO continuous_agg_max_mat_t SELECT i, i FROM generate_series('2019-09-09 1:00'::TIMESTAMPTZ, '2019-09-09 10:00', '1 hour') AS i; SET timescaledb.current_timestamp_mock = '2019-09-09 10:00 UTC'; -- first run create two materializations REFRESH MATERIALIZED VIEW max_mat_view_t; SELECT * FROM max_mat_view_t ORDER BY 1; -- repeated runs will eventually materialize all the data REFRESH MATERIALIZED VIEW max_mat_view_t; SELECT * FROM max_mat_view_t ORDER BY 1; REFRESH MATERIALIZED VIEW max_mat_view_t; SELECT * FROM max_mat_view_t ORDER BY 1; REFRESH MATERIALIZED VIEW max_mat_view_t; SELECT * FROM max_mat_view_t ORDER BY 1; -- regular timestamp CREATE TABLE continuous_agg_max_mat_timestamp(time TIMESTAMP); SELECT create_hypertable('continuous_agg_max_mat_timestamp', 'time'); CREATE VIEW max_mat_view_timestamp WITH (timescaledb.continuous, timescaledb.materialized_only=true, timescaledb.refresh_lag='-2 hours') AS SELECT time_bucket('2 hours', time) FROM continuous_agg_max_mat_timestamp GROUP BY 1; INSERT INTO continuous_agg_max_mat_timestamp SELECT generate_series('2019-09-09 1:00'::TIMESTAMPTZ, '2019-09-09 10:00', '1 hour'); -- first materializes everything REFRESH MATERIALIZED VIEW max_mat_view_timestamp; SELECT * FROM max_mat_view_timestamp ORDER BY 1; -- date CREATE TABLE continuous_agg_max_mat_date(time DATE); SELECT create_hypertable('continuous_agg_max_mat_date', 'time'); CREATE VIEW max_mat_view_date WITH (timescaledb.continuous, timescaledb.materialized_only=true, timescaledb.refresh_lag='-7 days') AS SELECT time_bucket('7 days', time) FROM continuous_agg_max_mat_date GROUP BY 1; INSERT INTO continuous_agg_max_mat_date SELECT generate_series('2019-09-01'::DATE, '2019-09-010 10:00', '1 day'); SET timescaledb.current_timestamp_mock = '2019-09-09 01:00 UTC'; --SET timescaledb.current_timestamp_mock = '2019-09-09 01:01:01 UTC'; -- first materializes everything REFRESH MATERIALIZED VIEW max_mat_view_date; SELECT * FROM max_mat_view_date ORDER BY 1; SELECT view_name, completed_threshold, invalidation_threshold, job_id, job_status, last_run_duration FROM timescaledb_information.continuous_aggregate_stats ORDER BY 1; SELECT view_name, refresh_lag, max_interval_per_job FROM timescaledb_information.continuous_aggregates ORDER BY 1; SET SESSION timezone TO 'EST'; SELECT view_name, completed_threshold, invalidation_threshold, job_id, job_status, last_run_duration FROM timescaledb_information.continuous_aggregate_stats ORDER BY 1; SELECT view_name, refresh_lag, max_interval_per_job FROM timescaledb_information.continuous_aggregates ORDER BY 1; -- test timezone is respected when materializing cagg with TIMESTAMP time column RESET timescaledb.current_timestamp_mock; RESET client_min_messages; SET SESSION timezone TO 'GMT+5'; CREATE TABLE timezone_test(time timestamp NOT NULL); SELECT table_name FROM create_hypertable('timezone_test','time'); INSERT INTO timezone_test VALUES (now() - '30m'::interval), (now()), (now() + '30m'::interval); CREATE VIEW timezone_test_summary WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('5m', time) FROM timezone_test GROUP BY 1; REFRESH MATERIALIZED VIEW timezone_test_summary; -- this must return 1 as only 1 row is in the materialization interval SELECT count(*) FROM timezone_test_summary; DROP TABLE timezone_test CASCADE; -- repeat test with timezone with negative offset SET SESSION timezone TO 'GMT-5'; CREATE TABLE timezone_test(time timestamp NOT NULL); SELECT table_name FROM create_hypertable('timezone_test','time'); INSERT INTO timezone_test VALUES (now() - '30m'::interval), (now()), (now() + '30m'::interval); CREATE VIEW timezone_test_summary WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('5m', time) FROM timezone_test GROUP BY 1; REFRESH MATERIALIZED VIEW timezone_test_summary; -- this must return 1 as only 1 row is in the materialization interval SELECT count(*) FROM timezone_test_summary; DROP TABLE timezone_test CASCADE; -- TESTS for integer based table to verify watermark limited by max value of time column and not by now CREATE TABLE continuous_agg_int(time BIGINT, data BIGINT); SELECT create_hypertable('continuous_agg_int', 'time', chunk_time_interval=> 10); CREATE OR REPLACE FUNCTION integer_now_continuous_agg_max() returns BIGINT LANGUAGE SQL STABLE as $$ SELECT BIGINT '9223372036854775807' $$; SELECT set_integer_now_func('continuous_agg_int', 'integer_now_continuous_agg_max'); CREATE VIEW continuous_agg_int_max WITH (timescaledb.continuous, timescaledb.refresh_lag='0') AS SELECT time_bucket('10', time), COUNT(data) as value FROM continuous_agg_int GROUP BY 1; INSERT INTO continuous_agg_int values (-10, 100), (1,100), (10, 100); select chunk_table, ranges from chunk_relation_size('continuous_agg_int'); REFRESH MATERIALIZED VIEW continuous_agg_int_max; REFRESH MATERIALIZED VIEW continuous_agg_int_max; REFRESH MATERIALIZED VIEW continuous_agg_int_max; select * from continuous_agg_int_max; --watermark is 20 SELECT view_name, completed_threshold, invalidation_threshold FROM timescaledb_information.continuous_aggregate_stats where view_name::text like 'continuous_agg_int_max'; -- TEST that watermark is limited by max value from data and not by now() CREATE TABLE continuous_agg_ts_max_t(timecol TIMESTAMPTZ, data integer); SELECT create_hypertable('continuous_agg_ts_max_t', 'timecol', chunk_time_interval=>'365 days'::interval); CREATE VIEW continuous_agg_ts_max_view WITH (timescaledb.continuous, timescaledb.max_interval_per_job='365 days', timescaledb.refresh_lag='-2 hours') AS SELECT time_bucket('2 hours', timecol), COUNT(data) as value FROM continuous_agg_ts_max_t GROUP BY 1; INSERT INTO continuous_agg_ts_max_t values ('1969-01-01 1:00'::timestamptz, 10); REFRESH MATERIALIZED VIEW continuous_agg_ts_max_view; SELECT view_name, completed_threshold, invalidation_threshold FROM timescaledb_information.continuous_aggregate_stats where view_name::text like 'continuous_agg_ts_max_view'; INSERT INTO continuous_agg_ts_max_t values ('1970-01-01 1:00'::timestamptz, 10); select chunk_table, ranges from chunk_relation_size('continuous_agg_ts_max_t'); REFRESH MATERIALIZED VIEW continuous_agg_ts_max_view; REFRESH MATERIALIZED VIEW continuous_agg_ts_max_view; SELECT view_name, completed_threshold, invalidation_threshold FROM timescaledb_information.continuous_aggregate_stats where view_name::text like 'continuous_agg_ts_max_view'; -- no more new data to materialize, threshold should not change REFRESH MATERIALIZED VIEW continuous_agg_ts_max_view; SELECT view_name, completed_threshold, invalidation_threshold FROM timescaledb_information.continuous_aggregate_stats where view_name::text like 'continuous_agg_ts_max_view';