diff --git a/CHANGELOG.md b/CHANGELOG.md index b589fd5d768..1e9afa70108 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ accidentally triggering the load of a previous DB version.** * #5218 Add role-level security to job error log * #5214 Fix use of prepared statement in async module * #5259 Lock down search_path in SPI calls +* #5255 Fix year not multiple of day/month in nested CAgg ## 2.9.2 (2023-01-26) diff --git a/tsl/src/continuous_aggs/create.c b/tsl/src/continuous_aggs/create.c index 3252486a417..4721f182551 100644 --- a/tsl/src/continuous_aggs/create.c +++ b/tsl/src/continuous_aggs/create.c @@ -1113,6 +1113,17 @@ get_bucket_width(CAggTimebucketInfo bucket_info) break; case INTERVALOID: { + /* + * epoch will treat year as 365.25 days. This leads to the unexpected + * result that year is not multiple of day or month, which is perceived + * as a bug. For that reason, we treat all months as 30 days regardless of year + */ + if (bucket_info.interval->month && !bucket_info.interval->day && + !bucket_info.interval->time) + { + bucket_info.interval->day = bucket_info.interval->month * DAYS_PER_MONTH; + bucket_info.interval->month = 0; + } Datum epoch = DirectFunctionCall2(interval_part, PointerGetDatum(cstring_to_text("epoch")), IntervalPGetDatum(bucket_info.interval)); diff --git a/tsl/test/expected/cagg_on_cagg.out b/tsl/test/expected/cagg_on_cagg.out index 8caa49f32d8..d352f8ff5a6 100644 --- a/tsl/test/expected/cagg_on_cagg.out +++ b/tsl/test/expected/cagg_on_cagg.out @@ -3652,3 +3652,406 @@ UNION ALL -- DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +-- test some intuitive intervals that should work but +-- were not working due to unix epochs +-- validation test for 1 year on top of one day +-- validation test for 1 year on top of 1 month +-- validation test for 1 year on top of 1 week +-- bug report 5231 +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 day\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_50.bucket, + _materialized_hypertable_50.temperature + FROM _timescaledb_internal._materialized_hypertable_50 + WHERE _materialized_hypertable_50.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(50)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 day'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(50)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 day'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_2TH_LEVEL + View "public.conditions_summary_2" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_51.bucket, + _materialized_hypertable_51.temperature + FROM _timescaledb_internal._materialized_hypertable_51 + WHERE _materialized_hypertable_51.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(51)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 year'::interval, conditions_summary_1.bucket) AS bucket, + sum(conditions_summary_1.temperature) AS temperature + FROM conditions_summary_1 + WHERE conditions_summary_1.bucket >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(51)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 year'::interval, conditions_summary_1.bucket)); + +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 day\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'3 month\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_52.bucket, + _materialized_hypertable_52.temperature + FROM _timescaledb_internal._materialized_hypertable_52 + WHERE _materialized_hypertable_52.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(52)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 day'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(52)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 day'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_2TH_LEVEL + View "public.conditions_summary_2" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_53.bucket, + _materialized_hypertable_53.temperature + FROM _timescaledb_internal._materialized_hypertable_53 + WHERE _materialized_hypertable_53.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(53)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 3 mons'::interval, conditions_summary_1.bucket) AS bucket, + sum(conditions_summary_1.temperature) AS temperature + FROM conditions_summary_1 + WHERE conditions_summary_1.bucket >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(53)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 3 mons'::interval, conditions_summary_1.bucket)); + +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 month\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_54.bucket, + _materialized_hypertable_54.temperature + FROM _timescaledb_internal._materialized_hypertable_54 + WHERE _materialized_hypertable_54.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(54)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 mon'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(54)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 mon'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_2TH_LEVEL + View "public.conditions_summary_2" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_55.bucket, + _materialized_hypertable_55.temperature + FROM _timescaledb_internal._materialized_hypertable_55 + WHERE _materialized_hypertable_55.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(55)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 year'::interval, conditions_summary_1.bucket) AS bucket, + sum(conditions_summary_1.temperature) AS temperature + FROM conditions_summary_1 + WHERE conditions_summary_1.bucket >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(55)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 year'::interval, conditions_summary_1.bucket)); + +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 week\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_56.bucket, + _materialized_hypertable_56.temperature + FROM _timescaledb_internal._materialized_hypertable_56 + WHERE _materialized_hypertable_56.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(56)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 7 days'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(56)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 7 days'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +psql:include/cagg_on_cagg_validations.sql:43: ERROR: cannot create continuous aggregate with incompatible bucket width +DETAIL: Time bucket width of "public.conditions_summary_2" [@ 360 days] should be multiple of the time bucket width of "public.conditions_summary_1" [@ 7 days]. +\d+ :CAGG_NAME_2TH_LEVEL +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +psql:include/cagg_on_cagg_validations.sql:53: NOTICE: materialized view "conditions_summary_2" does not exist, skipping +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 week\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 month\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_57.bucket, + _materialized_hypertable_57.temperature + FROM _timescaledb_internal._materialized_hypertable_57 + WHERE _materialized_hypertable_57.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(57)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 7 days'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(57)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 7 days'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +psql:include/cagg_on_cagg_validations.sql:43: ERROR: cannot create continuous aggregate with incompatible bucket width +DETAIL: Time bucket width of "public.conditions_summary_2" [@ 30 days] should be multiple of the time bucket width of "public.conditions_summary_1" [@ 7 days]. +\d+ :CAGG_NAME_2TH_LEVEL +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +psql:include/cagg_on_cagg_validations.sql:53: NOTICE: materialized view "conditions_summary_2" does not exist, skipping +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; diff --git a/tsl/test/expected/cagg_on_cagg_dist_ht.out b/tsl/test/expected/cagg_on_cagg_dist_ht.out index ac996e5760a..89ce0b91fac 100644 --- a/tsl/test/expected/cagg_on_cagg_dist_ht.out +++ b/tsl/test/expected/cagg_on_cagg_dist_ht.out @@ -3689,6 +3689,404 @@ UNION ALL -- DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +-- bug report 5231 +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 day\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_50.bucket, + _materialized_hypertable_50.temperature + FROM _timescaledb_internal._materialized_hypertable_50 + WHERE _materialized_hypertable_50.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(50)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 day'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(50)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 day'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_2TH_LEVEL + View "public.conditions_summary_2" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_51.bucket, + _materialized_hypertable_51.temperature + FROM _timescaledb_internal._materialized_hypertable_51 + WHERE _materialized_hypertable_51.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(51)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 year'::interval, conditions_summary_1.bucket) AS bucket, + sum(conditions_summary_1.temperature) AS temperature + FROM conditions_summary_1 + WHERE conditions_summary_1.bucket >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(51)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 year'::interval, conditions_summary_1.bucket)); + +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 day\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'3 month\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_52.bucket, + _materialized_hypertable_52.temperature + FROM _timescaledb_internal._materialized_hypertable_52 + WHERE _materialized_hypertable_52.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(52)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 day'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(52)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 day'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_2TH_LEVEL + View "public.conditions_summary_2" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_53.bucket, + _materialized_hypertable_53.temperature + FROM _timescaledb_internal._materialized_hypertable_53 + WHERE _materialized_hypertable_53.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(53)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 3 mons'::interval, conditions_summary_1.bucket) AS bucket, + sum(conditions_summary_1.temperature) AS temperature + FROM conditions_summary_1 + WHERE conditions_summary_1.bucket >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(53)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 3 mons'::interval, conditions_summary_1.bucket)); + +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 month\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_54.bucket, + _materialized_hypertable_54.temperature + FROM _timescaledb_internal._materialized_hypertable_54 + WHERE _materialized_hypertable_54.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(54)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 mon'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(54)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 mon'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_2TH_LEVEL + View "public.conditions_summary_2" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_55.bucket, + _materialized_hypertable_55.temperature + FROM _timescaledb_internal._materialized_hypertable_55 + WHERE _materialized_hypertable_55.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(55)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 1 year'::interval, conditions_summary_1.bucket) AS bucket, + sum(conditions_summary_1.temperature) AS temperature + FROM conditions_summary_1 + WHERE conditions_summary_1.bucket >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(55)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 1 year'::interval, conditions_summary_1.bucket)); + +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 week\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_56.bucket, + _materialized_hypertable_56.temperature + FROM _timescaledb_internal._materialized_hypertable_56 + WHERE _materialized_hypertable_56.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(56)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 7 days'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(56)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 7 days'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +psql:include/cagg_on_cagg_validations.sql:43: ERROR: cannot create continuous aggregate with incompatible bucket width +DETAIL: Time bucket width of "public.conditions_summary_2" [@ 360 days] should be multiple of the time bucket width of "public.conditions_summary_1" [@ 7 days]. +\d+ :CAGG_NAME_2TH_LEVEL +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +psql:include/cagg_on_cagg_validations.sql:53: NOTICE: materialized view "conditions_summary_2" does not exist, skipping +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 week\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 month\'' +\ir include/cagg_on_cagg_validations.sql +-- 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. +\set CAGG_NAME_1ST_LEVEL conditions_summary_1 +\set CAGG_NAME_2TH_LEVEL conditions_summary_2 +-- +-- CAGG on hypertable (1st level) +-- +CREATE MATERIALIZED VIEW :CAGG_NAME_1ST_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_1ST + time_bucket(:BUCKET_WIDTH_1ST, "time", :'BUCKET_TZNAME_1ST') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_1ST, "time") AS bucket, + \endif + SUM(temperature) AS temperature +FROM conditions +GROUP BY 1 +WITH NO DATA; +\d+ :CAGG_NAME_1ST_LEVEL + View "public.conditions_summary_1" + Column | Type | Collation | Nullable | Default | Storage | Description +-------------+--------------------------+-----------+----------+---------+---------+------------- + bucket | timestamp with time zone | | | | plain | + temperature | numeric | | | | main | +View definition: + SELECT _materialized_hypertable_57.bucket, + _materialized_hypertable_57.temperature + FROM _timescaledb_internal._materialized_hypertable_57 + WHERE _materialized_hypertable_57.bucket < COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(57)), '-infinity'::timestamp with time zone) +UNION ALL + SELECT time_bucket('@ 7 days'::interval, conditions."time", 'UTC'::text) AS bucket, + sum(conditions.temperature) AS temperature + FROM conditions + WHERE conditions."time" >= COALESCE(_timescaledb_internal.to_timestamp(_timescaledb_internal.cagg_watermark(57)), '-infinity'::timestamp with time zone) + GROUP BY (time_bucket('@ 7 days'::interval, conditions."time", 'UTC'::text)); + +-- +-- CAGG on CAGG (2th level) +-- +\set VERBOSITY default +\set ON_ERROR_STOP 0 +\echo :WARNING_MESSAGE +-- SHOULD WORK +CREATE MATERIALIZED VIEW :CAGG_NAME_2TH_LEVEL +WITH (timescaledb.continuous) AS +SELECT + \if :IS_TIME_DIMENSION_WITH_TIMEZONE_2TH + time_bucket(:BUCKET_WIDTH_2TH, "bucket", :'BUCKET_TZNAME_2TH') AS bucket, + \else + time_bucket(:BUCKET_WIDTH_2TH, "bucket") AS bucket, + \endif + SUM(temperature) AS temperature +FROM :CAGG_NAME_1ST_LEVEL +GROUP BY 1 +WITH NO DATA; +psql:include/cagg_on_cagg_validations.sql:43: ERROR: cannot create continuous aggregate with incompatible bucket width +DETAIL: Time bucket width of "public.conditions_summary_2" [@ 30 days] should be multiple of the time bucket width of "public.conditions_summary_1" [@ 7 days]. +\d+ :CAGG_NAME_2TH_LEVEL +\set ON_ERROR_STOP 1 +\set VERBOSITY terse +-- +-- Cleanup +-- +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_2TH_LEVEL; +psql:include/cagg_on_cagg_validations.sql:53: NOTICE: materialized view "conditions_summary_2" does not exist, skipping +DROP MATERIALIZED VIEW IF EXISTS :CAGG_NAME_1ST_LEVEL; -- Cleanup \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; DROP DATABASE :DATA_NODE_1; diff --git a/tsl/test/sql/cagg_on_cagg.sql b/tsl/test/sql/cagg_on_cagg.sql index 3ad978b9b24..1020e443d40 100644 --- a/tsl/test/sql/cagg_on_cagg.sql +++ b/tsl/test/sql/cagg_on_cagg.sql @@ -244,3 +244,30 @@ SET timezone TO 'UTC'; \set BUCKET_WIDTH_2TH 'INTERVAL \'1 month\'' \set WARNING_MESSAGE '-- SHOULD WORK' \ir include/cagg_on_cagg_validations.sql + +-- test some intuitive intervals that should work but +-- were not working due to unix epochs +-- validation test for 1 year on top of one day +-- validation test for 1 year on top of 1 month +-- validation test for 1 year on top of 1 week + +-- bug report 5231 +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 day\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql + +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 day\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'3 month\'' +\ir include/cagg_on_cagg_validations.sql + +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 month\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql + +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 week\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql + +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 week\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 month\'' +\ir include/cagg_on_cagg_validations.sql diff --git a/tsl/test/sql/cagg_on_cagg_dist_ht.sql b/tsl/test/sql/cagg_on_cagg_dist_ht.sql index 556b99c7aa9..5350e59e6f7 100644 --- a/tsl/test/sql/cagg_on_cagg_dist_ht.sql +++ b/tsl/test/sql/cagg_on_cagg_dist_ht.sql @@ -265,6 +265,27 @@ SET timezone TO 'UTC'; \set WARNING_MESSAGE '-- SHOULD WORK' \ir include/cagg_on_cagg_validations.sql +-- bug report 5231 +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 day\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql + +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 day\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'3 month\'' +\ir include/cagg_on_cagg_validations.sql + +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 month\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql + +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 week\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 year\'' +\ir include/cagg_on_cagg_validations.sql + +\set BUCKET_WIDTH_1ST 'INTERVAL \'1 week\'' +\set BUCKET_WIDTH_2TH 'INTERVAL \'1 month\'' +\ir include/cagg_on_cagg_validations.sql + -- Cleanup \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER; DROP DATABASE :DATA_NODE_1;