From 3794b97ed870bca29af5ae024e97bb034b5236c7 Mon Sep 17 00:00:00 2001 From: Jan Nidzwetzki Date: Thu, 15 Feb 2024 14:28:59 +0100 Subject: [PATCH] Fix refresh on empty CAgg with variable bucket So far, we have not handled CAggs with variable buckets correctly. The CAgg refresh on a hypertable without any data lead to the error message "timestamp out of range". This patch fixes the problem by declaring empty CAggs as up-to-date. --- .unreleased/bugfix_6660 | 1 + tsl/src/continuous_aggs/refresh.c | 15 ++++++++++++--- tsl/test/expected/cagg_invalidation.out | 23 +++++++++++++++++++++++ tsl/test/sql/cagg_invalidation.sql | 20 ++++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 .unreleased/bugfix_6660 diff --git a/.unreleased/bugfix_6660 b/.unreleased/bugfix_6660 new file mode 100644 index 00000000000..6a9fc996347 --- /dev/null +++ b/.unreleased/bugfix_6660 @@ -0,0 +1 @@ +Fixes: #6660 Fix refresh on empty CAgg with variable bucket diff --git a/tsl/src/continuous_aggs/refresh.c b/tsl/src/continuous_aggs/refresh.c index 7f713bc4ed8..c2078d40659 100644 --- a/tsl/src/continuous_aggs/refresh.c +++ b/tsl/src/continuous_aggs/refresh.c @@ -824,9 +824,18 @@ continuous_agg_refresh_internal(const ContinuousAgg *cagg, if (refresh_window.end > invalidation_threshold) refresh_window.end = invalidation_threshold; - /* Capping the end might have made the window 0, or negative, so - * nothing to refresh in that case */ - if (refresh_window.start >= refresh_window.end) + /* Capping the end might have made the window 0, or negative, so nothing to refresh in that + * case. + * + * For variable with buckets we use a refresh_window.start value that is lower than the + * -infinity value (ts_time_get_nobegin < ts_time_get_min). Therefore, the first check in the + * following if statement is not enough. If the invalidation_threshold returns the min_value for + * the data type, we end up with [nobegin, min_value] which is an invalid time interval. + * Therefore, we have also to check if the invalidation_threshold is defined. If not, no refresh + * is needed. */ + if ((refresh_window.start >= refresh_window.end) || + (IS_TIMESTAMP_TYPE(refresh_window.type) && + invalidation_threshold == ts_time_get_min(refresh_window.type))) { emit_up_to_date_notice(cagg, callctx); diff --git a/tsl/test/expected/cagg_invalidation.out b/tsl/test/expected/cagg_invalidation.out index 8e837f817f7..2f09d99796f 100644 --- a/tsl/test/expected/cagg_invalidation.out +++ b/tsl/test/expected/cagg_invalidation.out @@ -1226,3 +1226,26 @@ CALL refresh_continuous_aggregate('cond_10', 0, 200); WARNING: invalid value for session variable "timescaledb.materializations_per_refresh_window" DETAIL: Expected an integer but current value is "-". \set VERBOSITY terse +-- Test refresh with undefined invalidation threshold and variable sized buckets +CREATE TABLE timestamp_ht ( + time timestamptz NOT NULL, + value float +); +SELECT create_hypertable('timestamp_ht', 'time'); + create_hypertable +--------------------------- + (9,public,timestamp_ht,t) +(1 row) + +CREATE MATERIALIZED VIEW temperature_4h + WITH (timescaledb.continuous) AS + SELECT time_bucket('4 hour', time), avg(value) + FROM timestamp_ht + GROUP BY 1 ORDER BY 1; +NOTICE: continuous aggregate "temperature_4h" is already up-to-date +CREATE MATERIALIZED VIEW temperature_4h_2 + WITH (timescaledb.continuous) AS + SELECT time_bucket('4 hour', time, 'Europe/Berlin'), avg(value) + FROM timestamp_ht + GROUP BY 1 ORDER BY 1; +NOTICE: continuous aggregate "temperature_4h_2" is already up-to-date diff --git a/tsl/test/sql/cagg_invalidation.sql b/tsl/test/sql/cagg_invalidation.sql index d3fc2f77fc3..64ded224b3b 100644 --- a/tsl/test/sql/cagg_invalidation.sql +++ b/tsl/test/sql/cagg_invalidation.sql @@ -719,3 +719,23 @@ SET timescaledb.materializations_per_refresh_window='-'; INSERT INTO conditions VALUES (140, 1, 1.0); CALL refresh_continuous_aggregate('cond_10', 0, 200); \set VERBOSITY terse + +-- Test refresh with undefined invalidation threshold and variable sized buckets +CREATE TABLE timestamp_ht ( + time timestamptz NOT NULL, + value float +); + +SELECT create_hypertable('timestamp_ht', 'time'); + +CREATE MATERIALIZED VIEW temperature_4h + WITH (timescaledb.continuous) AS + SELECT time_bucket('4 hour', time), avg(value) + FROM timestamp_ht + GROUP BY 1 ORDER BY 1; + +CREATE MATERIALIZED VIEW temperature_4h_2 + WITH (timescaledb.continuous) AS + SELECT time_bucket('4 hour', time, 'Europe/Berlin'), avg(value) + FROM timestamp_ht + GROUP BY 1 ORDER BY 1;