@@ -299,5 +299,73 @@ CALL refresh_continuous_aggregate('mat_drop',NULL,NULL);
299299
300300SELECT drop_chunks(' drop_test' , NOW() - INTERVAL ' 7 days' );
301301
302+ -- Test that pending rows in the invalidation log catalogs survive the
303+ -- update/downgrade. Two continuous aggregates on one hypertable:
304+ -- refreshing only the first one moves hypertable-log entries into BOTH
305+ -- caggs' materialization logs but drains only the refreshed one,
306+ -- leaving a finite pending row for the second cagg; a final insert
307+ -- with no refresh afterwards leaves a pending row in the hypertable
308+ -- invalidation log.
309+ CREATE TABLE inval_log_test (time TIMESTAMPTZ NOT NULL , device TEXT NOT NULL , value INTEGER );
310+ SELECT create_hypertable(' inval_log_test' , ' time' , chunk_time_interval => INTERVAL ' 1 week' );
311+
312+ INSERT INTO inval_log_test
313+ SELECT ts, ' dev1' , 1
314+ FROM generate_series(' 2020-01-01 00:00:00+00' ::timestamptz ,
315+ ' 2020-01-10 00:00:00+00' ::timestamptz , ' 30 minutes' ) ts;
316+
317+ CREATE MATERIALIZED VIEW mat_invallog_1
318+ WITH (timescaledb .continuous , timescaledb .materialized_only = true)
319+ AS
320+ SELECT time_bucket(' 1 hour' , time ) AS bucket, device,
321+ count (* ) AS cnt
322+ FROM inval_log_test
323+ GROUP BY bucket, device WITH NO DATA;
324+
325+ CREATE MATERIALIZED VIEW mat_invallog_2
326+ WITH (timescaledb .continuous , timescaledb .materialized_only = true)
327+ AS
328+ SELECT time_bucket(' 1 day' , time ) AS bucket, device,
329+ count (* ) AS cnt
330+ FROM inval_log_test
331+ GROUP BY bucket, device WITH NO DATA;
332+
333+ CALL refresh_continuous_aggregate(' mat_invallog_1' , NULL , NULL );
334+ CALL refresh_continuous_aggregate(' mat_invallog_2' , NULL , NULL );
335+
336+ -- Below-threshold insert
337+ INSERT INTO inval_log_test
338+ SELECT ts, ' dev2' , 42
339+ FROM generate_series(' 2020-01-05 10:00:00+00' ::timestamptz ,
340+ ' 2020-01-05 12:00:00+00' ::timestamptz , ' 30 minutes' ) ts;
341+
342+ -- Refresh only the first cagg: moves ht invalidation log entries int
343+ -- materialization logs for both caggs, but drains only the refreshed one,
344+ -- leaving pending row for the second cagg.
345+ CALL refresh_continuous_aggregate(' mat_invallog_1' , NULL , NULL );
346+
347+ -- Below-threshold insert with no refresh afterwards, leaves ht invalidation
348+ -- log entry pending for both caggs.
349+ INSERT INTO inval_log_test
350+ SELECT ts, ' dev3' , 5
351+ FROM generate_series(' 2020-01-07 07:00:00+00' ::timestamptz ,
352+ ' 2020-01-07 09:00:00+00' ::timestamptz , ' 30 minutes' ) ts;
353+
354+ -- Snapshot invalidation log rows into a plain table so the post script can
355+ -- verify the live logs still hold the same content after the update rebuilt
356+ -- the catalogs.
357+ CREATE TABLE inval_log_snapshot AS
358+ SELECT ' hypertable' ::text AS log, h .table_name AS name,
359+ l .lowest_modified_value , l .greatest_modified_value
360+ FROM _timescaledb_catalog .continuous_aggs_hypertable_invalidation_log l
361+ JOIN _timescaledb_catalog .hypertable h ON h .id = l .hypertable_id
362+ WHERE h .table_name = ' inval_log_test'
363+ UNION ALL
364+ SELECT ' materialization' , ca .user_view_name ,
365+ l .lowest_modified_value , l .greatest_modified_value
366+ FROM _timescaledb_catalog .continuous_aggs_materialization_invalidation_log l
367+ JOIN _timescaledb_catalog .continuous_agg ca ON ca .mat_hypertable_id = l .materialization_id
368+ WHERE ca .user_view_name IN (' mat_invallog_1' , ' mat_invallog_2' );
369+
302370RESET timescaledb .enable_chunkwise_aggregation ;
303371RESET enable_hashagg;
0 commit comments