diff --git a/sql/time_bucket_ng.sql b/sql/time_bucket_ng.sql index eb63f2959..d1990a9c8 100644 --- a/sql/time_bucket_ng.sql +++ b/sql/time_bucket_ng.sql @@ -24,10 +24,10 @@ -- [2]: https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-TIMEZONES -- CREATE OR REPLACE FUNCTION timescaledb_experimental.time_bucket_ng(bucket_width INTERVAL, ts DATE) RETURNS DATE - AS '@MODULE_PATHNAME@', 'ts_time_bucket_ng' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT; + AS '@MODULE_PATHNAME@', 'ts_time_bucket_ng_date' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT; CREATE OR REPLACE FUNCTION timescaledb_experimental.time_bucket_ng(bucket_width INTERVAL, ts DATE, origin DATE) RETURNS DATE - AS '@MODULE_PATHNAME@', 'ts_time_bucket_ng' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT; + AS '@MODULE_PATHNAME@', 'ts_time_bucket_ng_date' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT; -- utility functions CREATE OR REPLACE FUNCTION timescaledb_experimental.time_bucket_ng(bucket_width INTERVAL, ts TIMESTAMP) RETURNS TIMESTAMP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 804bc9c5f..54eb69eeb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,7 +48,6 @@ set(SOURCES subspace_store.c tablespace.c time_bucket.c - time_bucket_ng.c time_utils.c custom_type_cache.c trigger.c diff --git a/src/time_bucket.c b/src/time_bucket.c index 071221a10..9dde1276c 100644 --- a/src/time_bucket.c +++ b/src/time_bucket.c @@ -292,3 +292,168 @@ ts_time_bucket_by_type(int64 interval, int64 timestamp, Oid timestamp_type) return ts_time_value_to_internal(time_bucketed, timestamp_type); } + +TS_FUNCTION_INFO_V1(ts_time_bucket_ng_timestamp); +TSDLLEXPORT Datum +ts_time_bucket_ng_timestamp(PG_FUNCTION_ARGS) +{ + DateADT ts_date; + Interval *interval = PG_GETARG_INTERVAL_P(0); + Timestamp timestamp = PG_GETARG_TIMESTAMP(1); + + if (interval->time != 0) + { + if (interval->month != 0) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("interval can't combine months with minutes or hours"))); + } + + /* + * Handle minutes, hours and days. + */ + Timestamp origin = (PG_NARGS() > 2 ? PG_GETARG_TIMESTAMP(2) : DEFAULT_ORIGIN); + Timestamp result; + int64 period = get_interval_period_timestamp_units(interval); + + if (TIMESTAMP_NOT_FINITE(timestamp)) + PG_RETURN_TIMESTAMP(timestamp); + + TIME_BUCKET_TS(period, timestamp, result, origin); + + PG_RETURN_TIMESTAMP(result); + } + + /* + * Discard any time information and work with the date. + */ + ts_date = DatumGetDateADT(DirectFunctionCall1(timestamp_date, PG_GETARG_DATUM(1))); + + if (PG_NARGS() > 2) + { + DateADT result; + DateADT origin = DatumGetDateADT(DirectFunctionCall1(timestamp_date, PG_GETARG_DATUM(2))); + result = DatumGetDateADT(DirectFunctionCall3(ts_time_bucket_ng_date, + PG_GETARG_DATUM(0), + DateADTGetDatum(ts_date), + DateADTGetDatum(origin))); + return DirectFunctionCall1(date_timestamp, DateADTGetDatum(result)); + } + else + { + DateADT result; + result = DatumGetDateADT(DirectFunctionCall2(ts_time_bucket_ng_date, + PG_GETARG_DATUM(0), + DateADTGetDatum(ts_date))); + return DirectFunctionCall1(date_timestamp, DateADTGetDatum(result)); + } +} + +TS_FUNCTION_INFO_V1(ts_time_bucket_ng_timestamptz); +TSDLLEXPORT Datum +ts_time_bucket_ng_timestamptz(PG_FUNCTION_ARGS) +{ + DateADT result; + Datum interval = PG_GETARG_DATUM(0); + DateADT ts_date = DatumGetDateADT(DirectFunctionCall1(timestamptz_date, PG_GETARG_DATUM(1))); + + if (PG_NARGS() > 2) + { + DateADT origin = DatumGetDateADT(DirectFunctionCall1(timestamptz_date, PG_GETARG_DATUM(2))); + result = DatumGetDateADT(DirectFunctionCall3(ts_time_bucket_ng_date, + interval, + DateADTGetDatum(ts_date), + DateADTGetDatum(origin))); + } + else + { + result = DatumGetDateADT( + DirectFunctionCall2(ts_time_bucket_ng_date, interval, DateADTGetDatum(ts_date))); + } + + return DirectFunctionCall1(date_timestamptz, DateADTGetDatum(result)); +} + +TS_FUNCTION_INFO_V1(ts_time_bucket_ng_date); +TSDLLEXPORT Datum +ts_time_bucket_ng_date(PG_FUNCTION_ARGS) +{ + Interval *interval = PG_GETARG_INTERVAL_P(0); + DateADT date = PG_GETARG_DATEADT(1); + DateADT origin_date = 0; + int origin_year = 2000, origin_month = 1, origin_day = 1; + int year, month, day; + int delta, bucket_number; + + if ((interval->time != 0) || ((interval->month != 0) && (interval->day != 0))) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("interval must be either days and weeks, or months and years"))); + } + + if ((interval->month == 0) && (interval->day == 0)) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("interval must be at least one day"))); + } + + if (PG_NARGS() > 2) + { + origin_date = PG_GETARG_DATUM(2); + j2date(origin_date + POSTGRES_EPOCH_JDATE, &origin_year, &origin_month, &origin_day); + } + + if ((origin_day != 1) && (interval->month != 0)) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("origin must be the first day of the month"))); + } + + if (DATE_NOT_FINITE(date)) + PG_RETURN_DATEADT(date); + + if (interval->month != 0) + { + /* Handle months and years */ + + j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day); + + if ((year < origin_year) || ((year == origin_year) && (month < origin_month))) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("origin must be before the given date"))); + } + + delta = (year * 12 + month) - (origin_year * 12 + origin_month); + bucket_number = delta / interval->month; + year = origin_year + (bucket_number * interval->month) / 12; + month = + (((origin_year * 12 + (origin_month - 1)) + (bucket_number * interval->month)) % 12) + + 1; + day = 1; + + date = date2j(year, month, day) - POSTGRES_EPOCH_JDATE; + } + else + { + /* Handle days and weeks */ + + if (date < origin_date) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("origin must be before the given date"))); + } + + delta = date - origin_date; + bucket_number = delta / interval->day; + date = bucket_number * interval->day; + } + + PG_RETURN_DATEADT(date); +} diff --git a/src/time_bucket.h b/src/time_bucket.h index 63b6ff2cd..a0be89992 100644 --- a/src/time_bucket.h +++ b/src/time_bucket.h @@ -18,5 +18,8 @@ extern TSDLLEXPORT Datum ts_date_bucket(PG_FUNCTION_ARGS); extern TSDLLEXPORT Datum ts_timestamp_bucket(PG_FUNCTION_ARGS); extern TSDLLEXPORT Datum ts_timestamptz_bucket(PG_FUNCTION_ARGS); extern TSDLLEXPORT int64 ts_time_bucket_by_type(int64 interval, int64 timestamp, Oid type); +extern TSDLLEXPORT Datum ts_time_bucket_ng_date(PG_FUNCTION_ARGS); +extern TSDLLEXPORT Datum ts_time_bucket_ng_timestamp(PG_FUNCTION_ARGS); +extern TSDLLEXPORT Datum ts_time_bucket_ng_timestamptz(PG_FUNCTION_ARGS); #endif /* TIMESCALEDB_TIME_BUCKET_H */ diff --git a/src/time_bucket_ng.c b/src/time_bucket_ng.c deleted file mode 100644 index 37077bab3..000000000 --- a/src/time_bucket_ng.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file and its contents are licensed under the Apache License 2.0. - * Please see the included NOTICE for copyright information and - * LICENSE-APACHE for a copy of the license. - */ -#include -#include -#include -#include - -#include "time_bucket_ng.h" - -TS_FUNCTION_INFO_V1(ts_time_bucket_ng); -TS_FUNCTION_INFO_V1(ts_time_bucket_ng_timestamp); -TS_FUNCTION_INFO_V1(ts_time_bucket_ng_timestamptz); - -TSDLLEXPORT Datum -ts_time_bucket_ng_timestamp(PG_FUNCTION_ARGS) -{ - DateADT result; - Datum interval = PG_GETARG_DATUM(0); - DateADT ts_date = DatumGetDateADT(DirectFunctionCall1(timestamp_date, PG_GETARG_DATUM(1))); - - if (PG_NARGS() > 2) - { - DateADT origin = DatumGetDateADT(DirectFunctionCall1(timestamp_date, PG_GETARG_DATUM(2))); - result = DatumGetDateADT(DirectFunctionCall3(ts_time_bucket_ng, - interval, - DateADTGetDatum(ts_date), - DateADTGetDatum(origin))); - } - else - { - result = DatumGetDateADT( - DirectFunctionCall2(ts_time_bucket_ng, interval, DateADTGetDatum(ts_date))); - } - - return DirectFunctionCall1(date_timestamp, DateADTGetDatum(result)); -} - -TSDLLEXPORT Datum -ts_time_bucket_ng_timestamptz(PG_FUNCTION_ARGS) -{ - DateADT result; - Datum interval = PG_GETARG_DATUM(0); - DateADT ts_date = DatumGetDateADT(DirectFunctionCall1(timestamptz_date, PG_GETARG_DATUM(1))); - - if (PG_NARGS() > 2) - { - DateADT origin = DatumGetDateADT(DirectFunctionCall1(timestamptz_date, PG_GETARG_DATUM(2))); - result = DatumGetDateADT(DirectFunctionCall3(ts_time_bucket_ng, - interval, - DateADTGetDatum(ts_date), - DateADTGetDatum(origin))); - } - else - { - result = DatumGetDateADT( - DirectFunctionCall2(ts_time_bucket_ng, interval, DateADTGetDatum(ts_date))); - } - - return DirectFunctionCall1(date_timestamptz, DateADTGetDatum(result)); -} - -TSDLLEXPORT Datum -ts_time_bucket_ng(PG_FUNCTION_ARGS) -{ - Interval *interval = PG_GETARG_INTERVAL_P(0); - DateADT date = PG_GETARG_DATEADT(1); - DateADT origin_date = 0; // 2000-01-01 - int origin_year = 2000, origin_month = 1, origin_day = 1; - int year, month, day; - int delta, bucket_number; - - if ((interval->time != 0) || ((interval->month != 0) && (interval->day != 0))) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("interval must be either days and weeks, or months and years"))); - } - - if ((interval->month == 0) && (interval->day == 0)) - { - /* - * This will be fixed in future versions of ts_time_bucket_ng(). - * The reason why it's not yet implemented is that we want to start - * experimenting with variable-sized buckets as soon as possible. - * We know that fixed-sized buckets work OK and adding corresponding - * logic will be trivial. - */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("interval must be at least one day"))); - } - - if (PG_NARGS() > 2) - { - origin_date = PG_GETARG_DATUM(2); - j2date(origin_date + POSTGRES_EPOCH_JDATE, &origin_year, &origin_month, &origin_day); - } - - if ((origin_day != 1) && (interval->month != 0)) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("origin must be the first day of the month"))); - } - - if (DATE_NOT_FINITE(date)) - PG_RETURN_DATEADT(date); - - if (interval->month != 0) - { - /* Handle months and years */ - - j2date(date + POSTGRES_EPOCH_JDATE, &year, &month, &day); - - if ((year < origin_year) || ((year == origin_year) && (month < origin_month))) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("origin must be before the given date"))); - } - - delta = (year * 12 + month) - (origin_year * 12 + origin_month); - bucket_number = delta / interval->month; - year = origin_year + (bucket_number * interval->month) / 12; - month = - (((origin_year * 12 + (origin_month - 1)) + (bucket_number * interval->month)) % 12) + - 1; - day = 1; - - date = date2j(year, month, day) - POSTGRES_EPOCH_JDATE; - } - else - { - /* Handle days and weeks */ - - if (date < origin_date) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("origin must be before the given date"))); - } - - delta = date - origin_date; - bucket_number = delta / interval->day; - date = bucket_number * interval->day; - } - - PG_RETURN_DATEADT(date); -} diff --git a/src/time_bucket_ng.h b/src/time_bucket_ng.h deleted file mode 100644 index 628434e97..000000000 --- a/src/time_bucket_ng.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This file and its contents are licensed under the Apache License 2.0. - * Please see the included NOTICE for copyright information and - * LICENSE-APACHE for a copy of the license. - */ -#ifndef TIMESCALEDB_DATE_TRUNC_H -#define TIMESCALEDB_DATE_TRUNC_H - -#include -#include - -#include "export.h" - -extern TSDLLEXPORT Datum ts_time_bucket_ng(PG_FUNCTION_ARGS); -extern TSDLLEXPORT Datum ts_time_bucket_ng_timestamp(PG_FUNCTION_ARGS); -extern TSDLLEXPORT Datum ts_time_bucket_ng_timestamptz(PG_FUNCTION_ARGS); - -#endif /* TIMESCALEDB_DATE_TRUNC_H */ diff --git a/test/expected/timestamp.out b/test/expected/timestamp.out index 5d47b8742..3dfb66132 100644 --- a/test/expected/timestamp.out +++ b/test/expected/timestamp.out @@ -1238,6 +1238,8 @@ SELECT timescaledb_experimental.time_bucket_ng('1 month', '2000-01-02' :: date, ERROR: origin must be before the given date SELECT timescaledb_experimental.time_bucket_ng('1 day', '2000-01-02' :: date, origin => '2001-01-01') AS result; ERROR: origin must be before the given date +SELECT timescaledb_experimental.time_bucket_ng('1 month 3 hours', '2021-11-22' :: timestamp) AS result; +ERROR: interval can't combine months with minutes or hours \set ON_ERROR_STOP 1 -- infinity SELECT timescaledb_experimental.time_bucket_ng('1 year', 'infinity' :: date) AS result; @@ -1271,6 +1273,48 @@ SELECT timescaledb_experimental.time_bucket_ng('1 year', '2021-11-22' :: timesta Tue Jun 01 00:00:00 2021 EDT (1 row) +-- Make sure time_bucket_ng() supports seconds, minutes, and hours. +-- We happen to know that the internal implementation is the same +-- as for time_bucket(), thus there is no reason to execute all the tests +-- we already have for time_bucket(). These two functions will most likely +-- be merged eventually anyway. +SELECT timescaledb_experimental.time_bucket_ng('30 seconds', '2021-07-12 12:34:56' :: timestamp) AS result; + result +-------------------------- + Mon Jul 12 12:34:30 2021 +(1 row) + +SELECT timescaledb_experimental.time_bucket_ng('15 minutes', '2021-07-12 12:34:56' :: timestamp) AS result; + result +-------------------------- + Mon Jul 12 12:30:00 2021 +(1 row) + +SELECT timescaledb_experimental.time_bucket_ng('6 hours', '2021-07-12 12:34:56' :: timestamp) AS result; + result +-------------------------- + Mon Jul 12 12:00:00 2021 +(1 row) + +-- Same as above, but with provided 'origin' argument. +SELECT timescaledb_experimental.time_bucket_ng('30 seconds', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result; + result +-------------------------- + Mon Jul 12 12:34:30 2021 +(1 row) + +SELECT timescaledb_experimental.time_bucket_ng('15 minutes', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result; + result +-------------------------- + Mon Jul 12 12:25:00 2021 +(1 row) + +SELECT timescaledb_experimental.time_bucket_ng('6 hours', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result; + result +-------------------------- + Mon Jul 12 12:10:00 2021 +(1 row) + -- N days / weeks buckets SELECT to_char(d, 'YYYY-MM-DD') AS d, to_char(timescaledb_experimental.time_bucket_ng('1 day', d), 'YYYY-MM-DD') AS d1, diff --git a/test/sql/timestamp.sql b/test/sql/timestamp.sql index 2a99b7467..c29d02cde 100644 --- a/test/sql/timestamp.sql +++ b/test/sql/timestamp.sql @@ -609,6 +609,7 @@ SELECT timescaledb_experimental.time_bucket_ng('0 days', '2001-02-03' :: date) A SELECT timescaledb_experimental.time_bucket_ng('1 month', '2001-02-03' :: date, origin => '2000-01-02') AS result; SELECT timescaledb_experimental.time_bucket_ng('1 month', '2000-01-02' :: date, origin => '2001-01-01') AS result; SELECT timescaledb_experimental.time_bucket_ng('1 day', '2000-01-02' :: date, origin => '2001-01-01') AS result; +SELECT timescaledb_experimental.time_bucket_ng('1 month 3 hours', '2021-11-22' :: timestamp) AS result; \set ON_ERROR_STOP 1 -- infinity @@ -620,6 +621,20 @@ SELECT timescaledb_experimental.time_bucket_ng('1 year', '2021-11-22' :: timesta SELECT timescaledb_experimental.time_bucket_ng('1 year', '2021-11-22' :: timestamp, origin => '2021-06-01') AS result; SELECT timescaledb_experimental.time_bucket_ng('1 year', '2021-11-22' :: timestamptz, origin => '2021-06-01') AS result; +-- Make sure time_bucket_ng() supports seconds, minutes, and hours. +-- We happen to know that the internal implementation is the same +-- as for time_bucket(), thus there is no reason to execute all the tests +-- we already have for time_bucket(). These two functions will most likely +-- be merged eventually anyway. +SELECT timescaledb_experimental.time_bucket_ng('30 seconds', '2021-07-12 12:34:56' :: timestamp) AS result; +SELECT timescaledb_experimental.time_bucket_ng('15 minutes', '2021-07-12 12:34:56' :: timestamp) AS result; +SELECT timescaledb_experimental.time_bucket_ng('6 hours', '2021-07-12 12:34:56' :: timestamp) AS result; + +-- Same as above, but with provided 'origin' argument. +SELECT timescaledb_experimental.time_bucket_ng('30 seconds', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result; +SELECT timescaledb_experimental.time_bucket_ng('15 minutes', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result; +SELECT timescaledb_experimental.time_bucket_ng('6 hours', '2021-07-12 12:34:56' :: timestamp, origin => '2021-07-12 12:10:00') AS result; + -- N days / weeks buckets SELECT to_char(d, 'YYYY-MM-DD') AS d, to_char(timescaledb_experimental.time_bucket_ng('1 day', d), 'YYYY-MM-DD') AS d1, diff --git a/tsl/test/expected/continuous_aggs_experimental.out b/tsl/test/expected/continuous_aggs_experimental.out index 2d954ffef..427f3dec3 100644 --- a/tsl/test/expected/continuous_aggs_experimental.out +++ b/tsl/test/expected/continuous_aggs_experimental.out @@ -53,3 +53,89 @@ ORDER BY bucket; DROP TABLE conditions CASCADE; NOTICE: drop cascades to 3 other objects NOTICE: drop cascades to 2 other objects +-- Make sure seconds, minutes, and hours can be used with caggs ('origin' is not +-- currently supported in caggs). +CREATE TABLE conditions( + tstamp TIMESTAMP NOT NULL, + city text NOT NULL, + temperature INT NOT NULL); +SELECT create_hypertable( + 'conditions', 'tstamp', + chunk_time_interval => INTERVAL '1 day' +); + create_hypertable +------------------------- + (3,public,conditions,t) +(1 row) + +INSERT INTO conditions (tstamp, city, temperature) VALUES + ('2021-06-14 12:30:00', 'Moscow', 26), + ('2021-06-14 12:30:10', 'Moscow', 22), + ('2021-06-14 12:30:20', 'Moscow', 24), + ('2021-06-14 12:30:30', 'Moscow', 24), + ('2021-06-14 12:30:40', 'Moscow', 27), + ('2021-06-14 12:30:50', 'Moscow', 28), + ('2021-06-14 12:31:10', 'Moscow', 30), + ('2021-06-14 12:31:20', 'Moscow', 31), + ('2021-06-14 12:31:30', 'Moscow', 34), + ('2021-06-14 12:31:40', 'Moscow', 34), + ('2021-06-14 12:31:50', 'Moscow', 34), + ('2021-06-14 12:32:00', 'Moscow', 32), + ('2021-06-14 12:32:10', 'Moscow', 32), + ('2021-06-14 12:32:20', 'Moscow', 31); +CREATE MATERIALIZED VIEW conditions_summary_30sec +WITH (timescaledb.continuous) AS +SELECT city, + timescaledb_experimental.time_bucket_ng('30 seconds', tstamp) AS bucket, + MIN(temperature), + MAX(temperature) +FROM conditions +GROUP BY city, bucket; +NOTICE: refreshing continuous aggregate "conditions_summary_30sec" +CREATE MATERIALIZED VIEW conditions_summary_1min +WITH (timescaledb.continuous) AS +SELECT city, + timescaledb_experimental.time_bucket_ng('1 minute', tstamp) AS bucket, + MIN(temperature), + MAX(temperature) +FROM conditions +GROUP BY city, bucket; +NOTICE: refreshing continuous aggregate "conditions_summary_1min" +CREATE MATERIALIZED VIEW conditions_summary_1hour +WITH (timescaledb.continuous) AS +SELECT city, + timescaledb_experimental.time_bucket_ng('1 hour', tstamp) AS bucket, + MIN(temperature), + MAX(temperature) +FROM conditions +GROUP BY city, bucket; +NOTICE: refreshing continuous aggregate "conditions_summary_1hour" +SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_30sec ORDER BY bucket; + city | to_char | min | max +--------+---------------------+-----+----- + Moscow | 2021-06-14 12:30:00 | 22 | 26 + Moscow | 2021-06-14 12:30:30 | 24 | 28 + Moscow | 2021-06-14 12:31:00 | 30 | 31 + Moscow | 2021-06-14 12:31:30 | 34 | 34 + Moscow | 2021-06-14 12:32:00 | 31 | 32 +(5 rows) + +SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_1min ORDER BY bucket; + city | to_char | min | max +--------+---------------------+-----+----- + Moscow | 2021-06-14 12:30:00 | 22 | 28 + Moscow | 2021-06-14 12:31:00 | 30 | 34 + Moscow | 2021-06-14 12:32:00 | 31 | 32 +(3 rows) + +SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_1hour ORDER BY bucket; + city | to_char | min | max +--------+---------------------+-----+----- + Moscow | 2021-06-14 12:00:00 | 22 | 34 +(1 row) + +DROP TABLE conditions CASCADE; +NOTICE: drop cascades to 9 other objects +NOTICE: drop cascades to table _timescaledb_internal._hyper_4_18_chunk +NOTICE: drop cascades to table _timescaledb_internal._hyper_5_19_chunk +NOTICE: drop cascades to table _timescaledb_internal._hyper_6_20_chunk diff --git a/tsl/test/sql/continuous_aggs_experimental.sql b/tsl/test/sql/continuous_aggs_experimental.sql index 91bdac2d9..705907c37 100644 --- a/tsl/test/sql/continuous_aggs_experimental.sql +++ b/tsl/test/sql/continuous_aggs_experimental.sql @@ -45,3 +45,64 @@ FROM conditions_summary_weekly ORDER BY bucket; DROP TABLE conditions CASCADE; + +-- Make sure seconds, minutes, and hours can be used with caggs ('origin' is not +-- currently supported in caggs). +CREATE TABLE conditions( + tstamp TIMESTAMP NOT NULL, + city text NOT NULL, + temperature INT NOT NULL); + +SELECT create_hypertable( + 'conditions', 'tstamp', + chunk_time_interval => INTERVAL '1 day' +); + +INSERT INTO conditions (tstamp, city, temperature) VALUES + ('2021-06-14 12:30:00', 'Moscow', 26), + ('2021-06-14 12:30:10', 'Moscow', 22), + ('2021-06-14 12:30:20', 'Moscow', 24), + ('2021-06-14 12:30:30', 'Moscow', 24), + ('2021-06-14 12:30:40', 'Moscow', 27), + ('2021-06-14 12:30:50', 'Moscow', 28), + ('2021-06-14 12:31:10', 'Moscow', 30), + ('2021-06-14 12:31:20', 'Moscow', 31), + ('2021-06-14 12:31:30', 'Moscow', 34), + ('2021-06-14 12:31:40', 'Moscow', 34), + ('2021-06-14 12:31:50', 'Moscow', 34), + ('2021-06-14 12:32:00', 'Moscow', 32), + ('2021-06-14 12:32:10', 'Moscow', 32), + ('2021-06-14 12:32:20', 'Moscow', 31); + +CREATE MATERIALIZED VIEW conditions_summary_30sec +WITH (timescaledb.continuous) AS +SELECT city, + timescaledb_experimental.time_bucket_ng('30 seconds', tstamp) AS bucket, + MIN(temperature), + MAX(temperature) +FROM conditions +GROUP BY city, bucket; + +CREATE MATERIALIZED VIEW conditions_summary_1min +WITH (timescaledb.continuous) AS +SELECT city, + timescaledb_experimental.time_bucket_ng('1 minute', tstamp) AS bucket, + MIN(temperature), + MAX(temperature) +FROM conditions +GROUP BY city, bucket; + +CREATE MATERIALIZED VIEW conditions_summary_1hour +WITH (timescaledb.continuous) AS +SELECT city, + timescaledb_experimental.time_bucket_ng('1 hour', tstamp) AS bucket, + MIN(temperature), + MAX(temperature) +FROM conditions +GROUP BY city, bucket; + +SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_30sec ORDER BY bucket; +SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_1min ORDER BY bucket; +SELECT city, to_char(bucket, 'YYYY-MM-DD HH:mi:ss'), min, max FROM conditions_summary_1hour ORDER BY bucket; + +DROP TABLE conditions CASCADE;