mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-16 18:43:18 +08:00
Add timezone support to time_bucket
This patch adds a new function time_bucket(period,timestamp,timezone) which supports bucketing for arbitrary timezones.
This commit is contained in:
parent
dc145b7485
commit
5d934baf1d
@ -11,6 +11,7 @@ accidentally triggering the load of a previous DB version.**
|
||||
* #4393 Support intervals with day component when constifying now()
|
||||
* #4397 Support intervals with month component when constifying now()
|
||||
* #4641 Allow bucketing by month in time_bucket
|
||||
* #4642 Add timezone support to time_bucket
|
||||
|
||||
**Bugfixes**
|
||||
* #4416 Handle TRUNCATE TABLE on chunks
|
||||
|
@ -23,6 +23,10 @@ CREATE OR REPLACE FUNCTION @extschema@.time_bucket(bucket_width INTERVAL, ts TIM
|
||||
CREATE OR REPLACE FUNCTION @extschema@.time_bucket(bucket_width INTERVAL, ts DATE, origin DATE) RETURNS DATE
|
||||
AS '@MODULE_PATHNAME@', 'ts_date_bucket' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT;
|
||||
|
||||
-- bucketing with timezone
|
||||
CREATE OR REPLACE FUNCTION @extschema@.time_bucket(bucket_width INTERVAL, ts TIMESTAMPTZ, timezone TEXT, origin TIMESTAMPTZ DEFAULT NULL, "offset" INTERVAL DEFAULT NULL) RETURNS TIMESTAMPTZ
|
||||
AS '@MODULE_PATHNAME@', 'ts_timestamptz_timezone_bucket' LANGUAGE C IMMUTABLE PARALLEL SAFE;
|
||||
|
||||
-- bucketing of int
|
||||
CREATE OR REPLACE FUNCTION @extschema@.time_bucket(bucket_width SMALLINT, ts SMALLINT) RETURNS SMALLINT
|
||||
AS '@MODULE_PATHNAME@', 'ts_int16_bucket' LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT;
|
||||
|
@ -154,7 +154,6 @@ GRANT SELECT ON _timescaledb_catalog.chunk TO PUBLIC;
|
||||
|
||||
-- end recreate _timescaledb_catalog.chunk table --
|
||||
|
||||
|
||||
ALTER TABLE _timescaledb_internal.bgw_job_stat
|
||||
DROP CONSTRAINT bgw_job_stat_job_id_fkey;
|
||||
ALTER TABLE _timescaledb_internal.bgw_policy_chunk_stats
|
||||
@ -246,3 +245,6 @@ CREATE FUNCTION @extschema@.alter_job(
|
||||
RETURNS TABLE (job_id INTEGER, schedule_interval INTERVAL, max_runtime INTERVAL, max_retries INTEGER, retry_period INTERVAL, scheduled BOOL, config JSONB, next_start TIMESTAMPTZ)
|
||||
AS '@MODULE_PATHNAME@', 'ts_job_alter'
|
||||
LANGUAGE C VOLATILE;
|
||||
|
||||
DROP FUNCTION @extschema@.time_bucket(INTERVAL, TIMESTAMPTZ, TEXT, TIMESTAMPTZ, INTERVAL);
|
||||
|
||||
|
@ -355,6 +355,11 @@ get_reindex_options(ReindexStmt *stmt)
|
||||
#define list_make5_int(x1, x2, x3, x4, x5) lappend_int(list_make4_int(x1, x2, x3, x4), x5)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* define lfifth macro for convenience
|
||||
*/
|
||||
#define lfifth(l) lfirst(list_nth_cell(l, 4))
|
||||
|
||||
/* PG13 removes the natts parameter from map_variable_attnos */
|
||||
#if PG13_LT
|
||||
#define map_variable_attnos_compat(node, varno, sublevels_up, map, natts, rowtype, found_wholerow) \
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <utils/selfuncs.h>
|
||||
#include <utils/syscache.h>
|
||||
|
||||
#include "compat/compat.h"
|
||||
#include "utils.h"
|
||||
#include "cache.h"
|
||||
#include "func_cache.h"
|
||||
@ -108,6 +109,22 @@ time_bucket_sort_transform(FuncExpr *func)
|
||||
return do_sort_transform(func);
|
||||
}
|
||||
|
||||
/*
|
||||
* time_bucket with timezone will always have 5 args. For the sort
|
||||
* optimization to apply all args need to be Const except timestamp.
|
||||
*/
|
||||
static Expr *
|
||||
time_bucket_tz_sort_transform(FuncExpr *func)
|
||||
{
|
||||
Assert(list_length(func->args) == 5);
|
||||
|
||||
if (!IsA(linitial((func)->args), Const) || !IsA(lthird(func->args), Const) ||
|
||||
!IsA(lfourth(func->args), Const) || !IsA(lfifth(func->args), Const))
|
||||
return (Expr *) func;
|
||||
|
||||
return do_sort_transform(func);
|
||||
}
|
||||
|
||||
/* For time_bucket this estimate currently works by seeing how many possible
|
||||
* buckets there will be if the data spans the entire hypertable. Note that
|
||||
* this is an overestimate.
|
||||
@ -295,6 +312,16 @@ static FuncInfo funcinfo[] = {
|
||||
.group_estimate = time_bucket_group_estimate,
|
||||
.sort_transform = time_bucket_sort_transform,
|
||||
},
|
||||
{
|
||||
.origin = ORIGIN_TIMESCALE,
|
||||
.is_bucketing_func = true,
|
||||
.allowed_in_cagg_definition = true,
|
||||
.funcname = "time_bucket",
|
||||
.nargs = 5,
|
||||
.arg_types = { INTERVALOID, TIMESTAMPTZOID, TEXTOID, TIMESTAMPTZOID, INTERVALOID },
|
||||
.group_estimate = time_bucket_group_estimate,
|
||||
.sort_transform = time_bucket_tz_sort_transform,
|
||||
},
|
||||
{
|
||||
.origin = ORIGIN_TIMESCALE_EXPERIMENTAL,
|
||||
.is_bucketing_func = true,
|
||||
|
@ -434,6 +434,9 @@ transform_time_bucket_comparison(PlannerInfo *root, OpExpr *op)
|
||||
{
|
||||
Interval *interval = DatumGetIntervalP(width->constvalue);
|
||||
|
||||
/*
|
||||
* Optimization can't be applied when interval has month component.
|
||||
*/
|
||||
if (interval->month != 0)
|
||||
return op;
|
||||
|
||||
@ -466,7 +469,7 @@ transform_time_bucket_comparison(PlannerInfo *root, OpExpr *op)
|
||||
Assert(width->consttype == INTERVALOID);
|
||||
|
||||
/*
|
||||
* intervals with month component are not supported by time_bucket
|
||||
* Optimization can't be applied when interval has month component.
|
||||
*/
|
||||
if (interval->month != 0)
|
||||
return op;
|
||||
@ -513,7 +516,7 @@ transform_time_bucket_comparison(PlannerInfo *root, OpExpr *op)
|
||||
Assert(width->consttype == INTERVALOID);
|
||||
|
||||
/*
|
||||
* intervals with month component are not supported by time_bucket
|
||||
* Optimization can't be applied when interval has month component.
|
||||
*/
|
||||
if (interval->month != 0)
|
||||
return op;
|
||||
|
@ -281,6 +281,64 @@ ts_timestamptz_bucket(PG_FUNCTION_ARGS)
|
||||
}
|
||||
}
|
||||
|
||||
TS_FUNCTION_INFO_V1(ts_timestamptz_timezone_bucket);
|
||||
|
||||
/*
|
||||
* time_bucket(bucket_width INTERVAL, ts TIMESTAMPTZ, timezone TEXT, origin TIMESTAMPTZ DEFAULT
|
||||
* NULL, "offset" INTERVAL DEFAULT NULL) RETURNS TIMESTAMPTZ
|
||||
*/
|
||||
TSDLLEXPORT Datum
|
||||
ts_timestamptz_timezone_bucket(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum period = PG_GETARG_DATUM(0);
|
||||
Datum timestamp = PG_GETARG_DATUM(1);
|
||||
Datum tzname = PG_GETARG_DATUM(2);
|
||||
|
||||
/*
|
||||
* When called from SQL we will always have 5 args because default values
|
||||
* will be filled in for missing arguments. When called from C with
|
||||
* DirectFunctionCall number of arguments might be less than 5.
|
||||
*/
|
||||
bool have_origin = PG_NARGS() > 3 && !PG_ARGISNULL(3);
|
||||
bool have_offset = PG_NARGS() > 4 && !PG_ARGISNULL(4);
|
||||
|
||||
/*
|
||||
* We need to check for NULL arguments here because the function cannot be
|
||||
* defined STRICT due to the optional arguments.
|
||||
*/
|
||||
if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2))
|
||||
PG_RETURN_NULL();
|
||||
|
||||
/* Convert to local timestamp according to timezone */
|
||||
timestamp = DirectFunctionCall2(timestamptz_zone, tzname, timestamp);
|
||||
if (have_offset)
|
||||
{
|
||||
/* Apply offset. */
|
||||
timestamp = DirectFunctionCall2(timestamp_mi_interval, timestamp, PG_GETARG_DATUM(4));
|
||||
}
|
||||
|
||||
if (have_origin)
|
||||
{
|
||||
Datum origin = DirectFunctionCall2(timestamptz_zone, tzname, PG_GETARG_DATUM(3));
|
||||
timestamp = DirectFunctionCall3(ts_timestamp_bucket, period, timestamp, origin);
|
||||
}
|
||||
else
|
||||
{
|
||||
timestamp = DirectFunctionCall2(ts_timestamp_bucket, period, timestamp);
|
||||
}
|
||||
|
||||
if (have_offset)
|
||||
{
|
||||
/* Remove offset. */
|
||||
timestamp = DirectFunctionCall2(timestamp_pl_interval, timestamp, PG_GETARG_DATUM(4));
|
||||
}
|
||||
|
||||
/* Convert back to timezone */
|
||||
timestamp = DirectFunctionCall2(timestamp_zone, tzname, timestamp);
|
||||
|
||||
PG_RETURN_DATUM(timestamp);
|
||||
}
|
||||
|
||||
static inline void
|
||||
check_period_is_daily(int64 period)
|
||||
{
|
||||
|
@ -17,6 +17,7 @@ extern TSDLLEXPORT Datum ts_int64_bucket(PG_FUNCTION_ARGS);
|
||||
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 Datum ts_timestamptz_timezone_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);
|
||||
|
@ -1541,46 +1541,88 @@ ts_continuous_agg_bucket_width(const ContinuousAgg *agg)
|
||||
* a common procedure used by ts_compute_* below.
|
||||
*/
|
||||
static Datum
|
||||
generic_time_bucket_ng(const ContinuousAggsBucketFunction *bf, Datum timestamp)
|
||||
generic_time_bucket(const ContinuousAggsBucketFunction *bf, Datum timestamp)
|
||||
{
|
||||
/* bf->timezone can't be NULL. If timezone is not specified, "" is stored */
|
||||
Assert(bf->timezone != NULL);
|
||||
|
||||
if (strlen(bf->timezone) > 0)
|
||||
if (!bf->experimental)
|
||||
{
|
||||
if (strlen(bf->timezone) > 0)
|
||||
{
|
||||
if (TIMESTAMP_NOT_FINITE(bf->origin))
|
||||
{
|
||||
/* using default origin */
|
||||
return DirectFunctionCall3(ts_timestamptz_timezone_bucket,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp,
|
||||
CStringGetTextDatum(bf->timezone));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* custom origin specified */
|
||||
return DirectFunctionCall4(ts_timestamptz_timezone_bucket,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp,
|
||||
CStringGetTextDatum(bf->timezone),
|
||||
TimestampTzGetDatum((TimestampTz) bf->origin));
|
||||
}
|
||||
}
|
||||
|
||||
if (TIMESTAMP_NOT_FINITE(bf->origin))
|
||||
{
|
||||
/* using default origin */
|
||||
return DirectFunctionCall3(ts_time_bucket_ng_timezone,
|
||||
return DirectFunctionCall2(ts_timestamp_bucket,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp,
|
||||
CStringGetTextDatum(bf->timezone));
|
||||
timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* custom origin specified */
|
||||
return DirectFunctionCall4(ts_time_bucket_ng_timezone_origin,
|
||||
return DirectFunctionCall3(ts_timestamp_bucket,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp,
|
||||
TimestampTzGetDatum((TimestampTz) bf->origin),
|
||||
CStringGetTextDatum(bf->timezone));
|
||||
TimestampGetDatum(bf->origin));
|
||||
}
|
||||
}
|
||||
|
||||
if (TIMESTAMP_NOT_FINITE(bf->origin))
|
||||
{
|
||||
/* using default origin */
|
||||
return DirectFunctionCall2(ts_time_bucket_ng_timestamp,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* custom origin specified */
|
||||
return DirectFunctionCall3(ts_time_bucket_ng_timestamp,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp,
|
||||
TimestampGetDatum(bf->origin));
|
||||
if (strlen(bf->timezone) > 0)
|
||||
{
|
||||
if (TIMESTAMP_NOT_FINITE(bf->origin))
|
||||
{
|
||||
/* using default origin */
|
||||
return DirectFunctionCall3(ts_time_bucket_ng_timezone,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp,
|
||||
CStringGetTextDatum(bf->timezone));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* custom origin specified */
|
||||
return DirectFunctionCall4(ts_time_bucket_ng_timezone_origin,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp,
|
||||
TimestampTzGetDatum((TimestampTz) bf->origin),
|
||||
CStringGetTextDatum(bf->timezone));
|
||||
}
|
||||
}
|
||||
|
||||
if (TIMESTAMP_NOT_FINITE(bf->origin))
|
||||
{
|
||||
/* using default origin */
|
||||
return DirectFunctionCall2(ts_time_bucket_ng_timestamp,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* custom origin specified */
|
||||
return DirectFunctionCall3(ts_time_bucket_ng_timestamp,
|
||||
IntervalPGetDatum(bf->bucket_width),
|
||||
timestamp,
|
||||
TimestampGetDatum(bf->origin));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1650,8 +1692,8 @@ ts_compute_inscribed_bucketed_refresh_window_variable(int64 *start, int64 *end,
|
||||
start_old = ts_internal_to_time_value(*start, TIMESTAMPOID);
|
||||
end_old = ts_internal_to_time_value(*end, TIMESTAMPOID);
|
||||
|
||||
start_new = generic_time_bucket_ng(bf, start_old);
|
||||
end_new = generic_time_bucket_ng(bf, end_old);
|
||||
start_new = generic_time_bucket(bf, start_old);
|
||||
end_new = generic_time_bucket(bf, end_old);
|
||||
|
||||
if (DatumGetTimestamp(start_new) != DatumGetTimestamp(start_old))
|
||||
{
|
||||
@ -1684,8 +1726,8 @@ ts_compute_circumscribed_bucketed_refresh_window_variable(int64 *start, int64 *e
|
||||
*/
|
||||
start_old = ts_internal_to_time_value(*start, TIMESTAMPOID);
|
||||
end_old = ts_internal_to_time_value(*end, TIMESTAMPOID);
|
||||
start_new = generic_time_bucket_ng(bf, start_old);
|
||||
end_new = generic_time_bucket_ng(bf, end_old);
|
||||
start_new = generic_time_bucket(bf, start_old);
|
||||
end_new = generic_time_bucket(bf, end_old);
|
||||
|
||||
if (DatumGetTimestamp(end_new) != DatumGetTimestamp(end_old))
|
||||
{
|
||||
@ -1716,7 +1758,7 @@ ts_compute_beginning_of_the_next_bucket_variable(int64 timeval,
|
||||
*/
|
||||
val_old = ts_internal_to_time_value(timeval, TIMESTAMPOID);
|
||||
|
||||
val_new = generic_time_bucket_ng(bf, val_old);
|
||||
val_new = generic_time_bucket(bf, val_old);
|
||||
val_new = generic_add_interval(bf, val_new);
|
||||
return ts_time_value_to_internal(val_new, TIMESTAMPOID);
|
||||
}
|
||||
|
@ -1284,6 +1284,117 @@ FROM generate_series('1990-01-03'::timestamptz,'1990-06-03'::timestamptz,'1month
|
||||
1990-06-03 00:00:00-04 | 1990-05-31 20:00:00-04 | 1990-04-30 20:00:00-04 | 1990-03-31 19:00:00-05 | 1990-05-31 20:00:00-04 | 1990-05-31 20:00:00-04 | 1990-04-30 20:00:00-04
|
||||
(6 rows)
|
||||
|
||||
---------------------------------------
|
||||
--- Test time_bucket with timezones ---
|
||||
---------------------------------------
|
||||
-- test NULL args
|
||||
SELECT
|
||||
time_bucket(NULL::interval,now(),'Europe/Berlin'),
|
||||
time_bucket('1day',NULL::timestamptz,'Europe/Berlin'),
|
||||
time_bucket('1day',now(),NULL::text),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin',NULL),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin','2020-04-01',NULL),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin',NULL,NULL),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin',"offset":=NULL::interval),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin',origin:=NULL::timestamptz);
|
||||
time_bucket | time_bucket | time_bucket | time_bucket | time_bucket | time_bucket | time_bucket | time_bucket
|
||||
-------------+-------------+-------------+------------------------+------------------------+------------------------+------------------------+------------------------
|
||||
| | | 2020-02-02 18:00:00-05 | 2020-02-03 00:00:00-05 | 2020-02-02 18:00:00-05 | 2020-02-02 18:00:00-05 | 2020-02-02 18:00:00-05
|
||||
(1 row)
|
||||
|
||||
SET datestyle TO ISO;
|
||||
SELECT
|
||||
time_bucket('1day', ts) AS "UTC",
|
||||
time_bucket('1day', ts, 'Europe/Berlin') AS "Berlin",
|
||||
time_bucket('1day', ts, 'Europe/London') AS "London",
|
||||
time_bucket('1day', ts, 'America/New_York') AS "New York",
|
||||
time_bucket('1day', ts, 'PST') AS "PST",
|
||||
time_bucket('1day', ts, current_setting('timezone')) AS "current"
|
||||
FROM generate_series('1999-12-31 17:00'::timestamptz,'2000-01-02 3:00'::timestamptz, '1hour'::interval) ts;
|
||||
UTC | Berlin | London | New York | PST | current
|
||||
------------------------+------------------------+------------------------+------------------------+------------------------+------------------------
|
||||
1999-12-30 19:00:00-05 | 1999-12-30 18:00:00-05 | 1999-12-30 19:00:00-05 | 1999-12-31 00:00:00-05 | 1999-12-31 03:00:00-05 | 1999-12-31 00:00:00-05
|
||||
1999-12-30 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-30 19:00:00-05 | 1999-12-31 00:00:00-05 | 1999-12-31 03:00:00-05 | 1999-12-31 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 1999-12-31 00:00:00-05 | 1999-12-31 03:00:00-05 | 1999-12-31 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 1999-12-31 00:00:00-05 | 1999-12-31 03:00:00-05 | 1999-12-31 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 1999-12-31 00:00:00-05 | 1999-12-31 03:00:00-05 | 1999-12-31 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 1999-12-31 00:00:00-05 | 1999-12-31 03:00:00-05 | 1999-12-31 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 1999-12-31 00:00:00-05 | 1999-12-31 03:00:00-05 | 1999-12-31 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 1999-12-31 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 1999-12-31 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 1999-12-31 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 2000-01-01 18:00:00-05 | 1999-12-31 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-01 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-02 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-02 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-02 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-02 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-02 00:00:00-05 | 2000-01-01 03:00:00-05 | 2000-01-02 00:00:00-05
|
||||
2000-01-01 19:00:00-05 | 2000-01-01 18:00:00-05 | 2000-01-01 19:00:00-05 | 2000-01-02 00:00:00-05 | 2000-01-02 03:00:00-05 | 2000-01-02 00:00:00-05
|
||||
(35 rows)
|
||||
|
||||
SELECT
|
||||
time_bucket('1month', ts) AS "UTC",
|
||||
time_bucket('1month', ts, 'Europe/Berlin') AS "Berlin",
|
||||
time_bucket('1month', ts, 'America/New_York') AS "New York",
|
||||
time_bucket('1month', ts, current_setting('timezone')) AS "current",
|
||||
time_bucket('2month', ts, current_setting('timezone')) AS "2m",
|
||||
time_bucket('2month', ts, current_setting('timezone'), '2000-02-01'::timestamp) AS "2m origin",
|
||||
time_bucket('2month', ts, current_setting('timezone'), "offset":='14 day'::interval) AS "2m offset",
|
||||
time_bucket('2month', ts, current_setting('timezone'), '2000-02-01'::timestamp, '7 day'::interval) AS "2m offset + origin"
|
||||
FROM generate_series('1999-12-01'::timestamptz,'2000-09-01'::timestamptz, '9 day'::interval) ts;
|
||||
UTC | Berlin | New York | current | 2m | 2m origin | 2m offset | 2m offset + origin
|
||||
------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------+------------------------
|
||||
1999-11-30 19:00:00-05 | 1999-11-30 18:00:00-05 | 1999-12-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-15 00:00:00-05 | 1999-10-08 00:00:00-04
|
||||
1999-11-30 19:00:00-05 | 1999-11-30 18:00:00-05 | 1999-12-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-15 00:00:00-05 | 1999-12-08 00:00:00-05
|
||||
1999-11-30 19:00:00-05 | 1999-11-30 18:00:00-05 | 1999-12-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-15 00:00:00-05 | 1999-12-08 00:00:00-05
|
||||
1999-11-30 19:00:00-05 | 1999-11-30 18:00:00-05 | 1999-12-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-15 00:00:00-05 | 1999-12-08 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 1999-11-15 00:00:00-05 | 1999-12-08 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 2000-01-15 00:00:00-05 | 1999-12-08 00:00:00-05
|
||||
1999-12-31 19:00:00-05 | 1999-12-31 18:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 1999-12-01 00:00:00-05 | 2000-01-15 00:00:00-05 | 1999-12-08 00:00:00-05
|
||||
2000-01-31 19:00:00-05 | 2000-01-31 18:00:00-05 | 2000-02-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-15 00:00:00-05 | 1999-12-08 00:00:00-05
|
||||
2000-01-31 19:00:00-05 | 2000-01-31 18:00:00-05 | 2000-02-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-15 00:00:00-05 | 2000-02-08 00:00:00-05
|
||||
2000-01-31 19:00:00-05 | 2000-01-31 18:00:00-05 | 2000-02-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-15 00:00:00-05 | 2000-02-08 00:00:00-05
|
||||
2000-01-31 19:00:00-05 | 2000-01-31 18:00:00-05 | 2000-02-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-15 00:00:00-05 | 2000-02-08 00:00:00-05
|
||||
2000-02-29 19:00:00-05 | 2000-02-29 18:00:00-05 | 2000-03-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-01-15 00:00:00-05 | 2000-02-08 00:00:00-05
|
||||
2000-02-29 19:00:00-05 | 2000-02-29 18:00:00-05 | 2000-03-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-03-15 00:00:00-05 | 2000-02-08 00:00:00-05
|
||||
2000-02-29 19:00:00-05 | 2000-02-29 18:00:00-05 | 2000-03-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-02-01 00:00:00-05 | 2000-03-15 00:00:00-05 | 2000-02-08 00:00:00-05
|
||||
2000-03-31 19:00:00-05 | 2000-03-31 17:00:00-05 | 2000-04-01 00:00:00-05 | 2000-04-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-04-01 00:00:00-05 | 2000-03-15 00:00:00-05 | 2000-02-08 00:00:00-05
|
||||
2000-03-31 19:00:00-05 | 2000-03-31 17:00:00-05 | 2000-04-01 00:00:00-05 | 2000-04-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-04-01 00:00:00-05 | 2000-03-15 00:00:00-05 | 2000-04-08 00:00:00-04
|
||||
2000-03-31 19:00:00-05 | 2000-03-31 17:00:00-05 | 2000-04-01 00:00:00-05 | 2000-04-01 00:00:00-05 | 2000-03-01 00:00:00-05 | 2000-04-01 00:00:00-05 | 2000-03-15 00:00:00-05 | 2000-04-08 00:00:00-04
|
||||
2000-04-30 20:00:00-04 | 2000-04-30 18:00:00-04 | 2000-05-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-04-01 00:00:00-05 | 2000-03-15 00:00:00-05 | 2000-04-08 00:00:00-04
|
||||
2000-04-30 20:00:00-04 | 2000-04-30 18:00:00-04 | 2000-05-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-04-01 00:00:00-05 | 2000-03-15 00:00:00-05 | 2000-04-08 00:00:00-04
|
||||
2000-04-30 20:00:00-04 | 2000-04-30 18:00:00-04 | 2000-05-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-04-01 00:00:00-05 | 2000-05-15 00:00:00-04 | 2000-04-08 00:00:00-04
|
||||
2000-04-30 20:00:00-04 | 2000-04-30 18:00:00-04 | 2000-05-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-04-01 00:00:00-05 | 2000-05-15 00:00:00-04 | 2000-04-08 00:00:00-04
|
||||
2000-05-31 20:00:00-04 | 2000-05-31 18:00:00-04 | 2000-06-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-05-15 00:00:00-04 | 2000-04-08 00:00:00-04
|
||||
2000-05-31 20:00:00-04 | 2000-05-31 18:00:00-04 | 2000-06-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-05-15 00:00:00-04 | 2000-06-08 00:00:00-04
|
||||
2000-05-31 20:00:00-04 | 2000-05-31 18:00:00-04 | 2000-06-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-05-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-05-15 00:00:00-04 | 2000-06-08 00:00:00-04
|
||||
2000-06-30 20:00:00-04 | 2000-06-30 18:00:00-04 | 2000-07-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-05-15 00:00:00-04 | 2000-06-08 00:00:00-04
|
||||
2000-06-30 20:00:00-04 | 2000-06-30 18:00:00-04 | 2000-07-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-05-15 00:00:00-04 | 2000-06-08 00:00:00-04
|
||||
2000-06-30 20:00:00-04 | 2000-06-30 18:00:00-04 | 2000-07-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-07-15 00:00:00-04 | 2000-06-08 00:00:00-04
|
||||
2000-06-30 20:00:00-04 | 2000-06-30 18:00:00-04 | 2000-07-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-06-01 00:00:00-04 | 2000-07-15 00:00:00-04 | 2000-06-08 00:00:00-04
|
||||
2000-07-31 20:00:00-04 | 2000-07-31 18:00:00-04 | 2000-08-01 00:00:00-04 | 2000-08-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-08-01 00:00:00-04 | 2000-07-15 00:00:00-04 | 2000-08-08 00:00:00-04
|
||||
2000-07-31 20:00:00-04 | 2000-07-31 18:00:00-04 | 2000-08-01 00:00:00-04 | 2000-08-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-08-01 00:00:00-04 | 2000-07-15 00:00:00-04 | 2000-08-08 00:00:00-04
|
||||
2000-07-31 20:00:00-04 | 2000-07-31 18:00:00-04 | 2000-08-01 00:00:00-04 | 2000-08-01 00:00:00-04 | 2000-07-01 00:00:00-04 | 2000-08-01 00:00:00-04 | 2000-07-15 00:00:00-04 | 2000-08-08 00:00:00-04
|
||||
(31 rows)
|
||||
|
||||
RESET datestyle;
|
||||
------------------------------------------------------------
|
||||
--- Test timescaledb_experimental.time_bucket_ng function --
|
||||
|
@ -634,6 +634,44 @@ SELECT
|
||||
time_bucket('3 month', time, '2000-02-01'::timestamptz) AS "3m origin"
|
||||
FROM generate_series('1990-01-03'::timestamptz,'1990-06-03'::timestamptz,'1month'::interval) time;
|
||||
|
||||
---------------------------------------
|
||||
--- Test time_bucket with timezones ---
|
||||
---------------------------------------
|
||||
|
||||
-- test NULL args
|
||||
SELECT
|
||||
time_bucket(NULL::interval,now(),'Europe/Berlin'),
|
||||
time_bucket('1day',NULL::timestamptz,'Europe/Berlin'),
|
||||
time_bucket('1day',now(),NULL::text),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin',NULL),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin','2020-04-01',NULL),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin',NULL,NULL),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin',"offset":=NULL::interval),
|
||||
time_bucket('1day','2020-02-03','Europe/Berlin',origin:=NULL::timestamptz);
|
||||
|
||||
SET datestyle TO ISO;
|
||||
SELECT
|
||||
time_bucket('1day', ts) AS "UTC",
|
||||
time_bucket('1day', ts, 'Europe/Berlin') AS "Berlin",
|
||||
time_bucket('1day', ts, 'Europe/London') AS "London",
|
||||
time_bucket('1day', ts, 'America/New_York') AS "New York",
|
||||
time_bucket('1day', ts, 'PST') AS "PST",
|
||||
time_bucket('1day', ts, current_setting('timezone')) AS "current"
|
||||
|
||||
FROM generate_series('1999-12-31 17:00'::timestamptz,'2000-01-02 3:00'::timestamptz, '1hour'::interval) ts;
|
||||
|
||||
SELECT
|
||||
time_bucket('1month', ts) AS "UTC",
|
||||
time_bucket('1month', ts, 'Europe/Berlin') AS "Berlin",
|
||||
time_bucket('1month', ts, 'America/New_York') AS "New York",
|
||||
time_bucket('1month', ts, current_setting('timezone')) AS "current",
|
||||
time_bucket('2month', ts, current_setting('timezone')) AS "2m",
|
||||
time_bucket('2month', ts, current_setting('timezone'), '2000-02-01'::timestamp) AS "2m origin",
|
||||
time_bucket('2month', ts, current_setting('timezone'), "offset":='14 day'::interval) AS "2m offset",
|
||||
time_bucket('2month', ts, current_setting('timezone'), '2000-02-01'::timestamp, '7 day'::interval) AS "2m offset + origin"
|
||||
|
||||
FROM generate_series('1999-12-01'::timestamptz,'2000-09-01'::timestamptz, '9 day'::interval) ts;
|
||||
|
||||
RESET datestyle;
|
||||
|
||||
------------------------------------------------------------
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <catalog/indexing.h>
|
||||
#include <catalog/pg_aggregate.h>
|
||||
#include <catalog/pg_collation.h>
|
||||
#include <catalog/pg_namespace.h>
|
||||
#include <catalog/pg_trigger.h>
|
||||
#include <catalog/pg_type.h>
|
||||
#include <catalog/toasting.h>
|
||||
@ -184,6 +185,7 @@ typedef struct CAggTimebucketInfo
|
||||
Interval *interval; /* stores the interval, NULL if not specified */
|
||||
const char *timezone; /* the name of the timezone, NULL if not specified */
|
||||
|
||||
FuncExpr *bucket_func; /* function call expr of the bucketing function */
|
||||
/*
|
||||
* Custom origin value stored as UTC timestamp.
|
||||
* If not specified, stores infinity.
|
||||
@ -712,6 +714,24 @@ caggtimebucketinfo_init(CAggTimebucketInfo *src, int32 hypertable_id, Oid hypert
|
||||
TIMESTAMP_NOBEGIN(src->origin); /* origin is not specified by default */
|
||||
}
|
||||
|
||||
static Const *
|
||||
check_time_bucket_argument(Node *arg, char *position)
|
||||
{
|
||||
if (IsA(arg, NamedArgExpr))
|
||||
arg = (Node *) castNode(NamedArgExpr, arg)->arg;
|
||||
|
||||
Node *expr = eval_const_expressions(NULL, arg);
|
||||
|
||||
if (!IsA(expr, Const))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("only immutable expressions allowed in time bucket function"),
|
||||
errhint("Use an immutable expression as %s argument to the time bucket function.",
|
||||
position)));
|
||||
|
||||
return castNode(Const, expr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the group-by clauses has exactly 1 time_bucket(.., <col>) where
|
||||
* <col> is the hypertable's partitioning column and other invariants. Then fill
|
||||
@ -722,6 +742,7 @@ caggtimebucket_validate(CAggTimebucketInfo *tbinfo, List *groupClause, List *tar
|
||||
{
|
||||
ListCell *l;
|
||||
bool found = false;
|
||||
bool custom_origin = false;
|
||||
|
||||
/* Make sure tbinfo was initialized. This assumption is used below. */
|
||||
Assert(tbinfo->bucket_width == 0);
|
||||
@ -737,11 +758,18 @@ caggtimebucket_validate(CAggTimebucketInfo *tbinfo, List *groupClause, List *tar
|
||||
FuncExpr *fe = ((FuncExpr *) tle->expr);
|
||||
Node *width_arg;
|
||||
Node *col_arg;
|
||||
Node *tz_arg;
|
||||
|
||||
if (!function_allowed_in_cagg_definition(fe->funcid))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* offset variants of time_bucket functions are not
|
||||
* supported at the moment.
|
||||
*/
|
||||
if (list_length(fe->args) >= 5 ||
|
||||
(list_length(fe->args) == 4 && exprType(lfourth(fe->args)) == INTERVALOID))
|
||||
continue;
|
||||
|
||||
if (found)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
@ -750,6 +778,8 @@ caggtimebucket_validate(CAggTimebucketInfo *tbinfo, List *groupClause, List *tar
|
||||
else
|
||||
found = true;
|
||||
|
||||
tbinfo->bucket_func = fe;
|
||||
|
||||
/*only column allowed : time_bucket('1day', <column> ) */
|
||||
col_arg = lsecond(fe->args);
|
||||
|
||||
@ -759,54 +789,12 @@ caggtimebucket_validate(CAggTimebucketInfo *tbinfo, List *groupClause, List *tar
|
||||
errmsg(
|
||||
"time bucket function must reference a hypertable dimension column")));
|
||||
|
||||
if (list_length(fe->args) == 4)
|
||||
{
|
||||
/*
|
||||
* Timezone and custom origin are specified. In this clause we
|
||||
* save only the timezone. Origin is processed in the following
|
||||
* clause.
|
||||
*/
|
||||
tz_arg = eval_const_expressions(NULL, lfourth(fe->args));
|
||||
|
||||
if (!IsA(tz_arg, Const))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("only immutable expressions allowed in time bucket function"),
|
||||
errhint("Use an immutable expression as fourth argument"
|
||||
" to the time bucket function.")));
|
||||
|
||||
Const *tz = castNode(Const, tz_arg);
|
||||
|
||||
/* This is assured by function_allowed_in_cagg_definition() above. */
|
||||
Assert(tz->consttype == TEXTOID);
|
||||
const char *tz_name = TextDatumGetCString(tz->constvalue);
|
||||
if (!ts_is_valid_timezone_name(tz_name))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid timezone name \"%s\"", tz_name)));
|
||||
}
|
||||
|
||||
tbinfo->timezone = tz_name;
|
||||
tbinfo->bucket_width = BUCKET_WIDTH_VARIABLE;
|
||||
}
|
||||
|
||||
if (list_length(fe->args) >= 3)
|
||||
{
|
||||
tz_arg = eval_const_expressions(NULL, lthird(fe->args));
|
||||
if (!IsA(tz_arg, Const))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("only immutable expressions allowed in time bucket function"),
|
||||
errhint("Use an immutable expression as third argument"
|
||||
" to the time bucket function.")));
|
||||
|
||||
Const *tz = castNode(Const, tz_arg);
|
||||
if ((tz->consttype == TEXTOID) && (list_length(fe->args) == 3))
|
||||
Const *arg = check_time_bucket_argument(lthird(fe->args), "third");
|
||||
if (exprType((Node *) arg) == TEXTOID)
|
||||
{
|
||||
/* Timezone specified */
|
||||
const char *tz_name = TextDatumGetCString(tz->constvalue);
|
||||
|
||||
const char *tz_name = TextDatumGetCString(arg->constvalue);
|
||||
if (!ts_is_valid_timezone_name(tz_name))
|
||||
{
|
||||
ereport(ERROR,
|
||||
@ -817,53 +805,71 @@ caggtimebucket_validate(CAggTimebucketInfo *tbinfo, List *groupClause, List *tar
|
||||
tbinfo->timezone = tz_name;
|
||||
tbinfo->bucket_width = BUCKET_WIDTH_VARIABLE;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
if (list_length(fe->args) >= 4)
|
||||
{
|
||||
Const *arg = check_time_bucket_argument(lfourth(fe->args), "fourth");
|
||||
if (exprType((Node *) arg) == TEXTOID)
|
||||
{
|
||||
/*
|
||||
* Custom origin specified. This is always treated as
|
||||
* a variable-sized bucket case.
|
||||
*/
|
||||
const char *tz_name = TextDatumGetCString(arg->constvalue);
|
||||
if (!ts_is_valid_timezone_name(tz_name))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid timezone name \"%s\"", tz_name)));
|
||||
}
|
||||
|
||||
tbinfo->timezone = tz_name;
|
||||
tbinfo->bucket_width = BUCKET_WIDTH_VARIABLE;
|
||||
|
||||
if (tz->constisnull)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid origin value: null")));
|
||||
}
|
||||
|
||||
switch (tz->consttype)
|
||||
{
|
||||
case DATEOID:
|
||||
tbinfo->origin = DatumGetTimestamp(
|
||||
DirectFunctionCall1(date_timestamp, tz->constvalue));
|
||||
break;
|
||||
case TIMESTAMPOID:
|
||||
tbinfo->origin = DatumGetTimestamp(tz->constvalue);
|
||||
break;
|
||||
case TIMESTAMPTZOID:
|
||||
tbinfo->origin = DatumGetTimestampTz(tz->constvalue);
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* This shouldn't happen. But if somehow it does
|
||||
* make sure the execution will stop here even in
|
||||
* the Release build.
|
||||
*/
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("unsupported time bucket function")));
|
||||
}
|
||||
|
||||
if (TIMESTAMP_NOT_FINITE(tbinfo->origin))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid origin value: infinity")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check for custom origin */
|
||||
switch (exprType(col_arg))
|
||||
{
|
||||
case DATEOID:
|
||||
/* origin is always 3rd arg for date variants */
|
||||
if (list_length(fe->args) == 3)
|
||||
{
|
||||
custom_origin = true;
|
||||
tbinfo->origin = DatumGetTimestamp(
|
||||
DirectFunctionCall1(date_timestamp,
|
||||
castNode(Const, lthird(fe->args))->constvalue));
|
||||
}
|
||||
break;
|
||||
case TIMESTAMPOID:
|
||||
/* origin is always 3rd arg for timestamp variants */
|
||||
if (list_length(fe->args) == 3)
|
||||
{
|
||||
custom_origin = true;
|
||||
tbinfo->origin =
|
||||
DatumGetTimestamp(castNode(Const, lthird(fe->args))->constvalue);
|
||||
}
|
||||
break;
|
||||
case TIMESTAMPTZOID:
|
||||
/* origin can be 3rd or 4th arg for timestamptz variants */
|
||||
if (list_length(fe->args) >= 3 && exprType(lthird(fe->args)) == TIMESTAMPTZOID)
|
||||
{
|
||||
custom_origin = true;
|
||||
tbinfo->origin =
|
||||
DatumGetTimestampTz(castNode(Const, lthird(fe->args))->constvalue);
|
||||
}
|
||||
else if (list_length(fe->args) >= 4 &&
|
||||
exprType(lfourth(fe->args)) == TIMESTAMPTZOID)
|
||||
{
|
||||
custom_origin = true;
|
||||
tbinfo->origin =
|
||||
DatumGetTimestampTz(castNode(Const, lfourth(fe->args))->constvalue);
|
||||
}
|
||||
}
|
||||
if (custom_origin && TIMESTAMP_NOT_FINITE(tbinfo->origin))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid origin value: infinity")));
|
||||
}
|
||||
|
||||
/*
|
||||
* We constify width expression here so any immutable expression will be allowed
|
||||
* otherwise it would make it harder to create caggs for hypertables with e.g. int8
|
||||
@ -895,35 +901,9 @@ caggtimebucket_validate(CAggTimebucketInfo *tbinfo, List *groupClause, List *tar
|
||||
errhint("Use an immutable expression as first argument"
|
||||
" to the time bucket function.")));
|
||||
|
||||
if ((tbinfo->bucket_width == BUCKET_WIDTH_VARIABLE) && (tbinfo->interval->month != 0))
|
||||
if (tbinfo->interval && tbinfo->interval->month)
|
||||
{
|
||||
/* Monthly buckets case */
|
||||
if (!TIMESTAMP_NOT_FINITE(tbinfo->origin))
|
||||
{
|
||||
/*
|
||||
* Origin was specified - make sure it's the first day of the month.
|
||||
* If a timezone was specified the check should be done in this timezone.
|
||||
*/
|
||||
Timestamp origin = tbinfo->origin;
|
||||
if (tbinfo->timezone != NULL)
|
||||
{
|
||||
/* The code is equal to 'timestamptz AT TIME ZONE tzname'. */
|
||||
origin = DatumGetTimestamp(
|
||||
DirectFunctionCall2(timestamptz_zone,
|
||||
CStringGetTextDatum(tbinfo->timezone),
|
||||
TimestampTzGetDatum((TimestampTz) origin)));
|
||||
}
|
||||
|
||||
const char *day =
|
||||
TextDatumGetCString(DirectFunctionCall2(timestamp_to_char,
|
||||
TimestampGetDatum(origin),
|
||||
CStringGetTextDatum("DD")));
|
||||
if (strcmp(day, "01") != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("for monthly buckets origin must be the first day of the "
|
||||
"month")));
|
||||
}
|
||||
tbinfo->bucket_width = BUCKET_WIDTH_VARIABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2312,18 +2292,17 @@ cagg_create(const CreateTableAsStmt *create_stmt, ViewStmt *stmt, Query *panquer
|
||||
}
|
||||
|
||||
/*
|
||||
* `experimental` = true and `name` = "time_bucket_ng" are hardcoded
|
||||
* rather than extracted from the query. We happen to know that
|
||||
* monthly buckets can currently be created only with time_bucket_ng(),
|
||||
* thus these values are correct. Besides, they are not used for
|
||||
* These values are not used for
|
||||
* anything except Assert's yet for the same reasons. Once the design
|
||||
* of variable-sized buckets is finalized we will have a better idea
|
||||
* of what schema is needed exactly. Until then the choice was made
|
||||
* in favor of the most generic schema that can be optimized later.
|
||||
*/
|
||||
create_bucket_function_catalog_entry(materialize_hypertable_id,
|
||||
true,
|
||||
"time_bucket_ng",
|
||||
get_func_namespace(
|
||||
origquery_ht->bucket_func->funcid) !=
|
||||
PG_PUBLIC_NAMESPACE,
|
||||
get_func_name(origquery_ht->bucket_func->funcid),
|
||||
bucket_width,
|
||||
origin,
|
||||
origquery_ht->timezone);
|
||||
|
@ -410,3 +410,58 @@ SELECT relname FROM pg_class WHERE oid = :mat_table;
|
||||
-- Cleanup
|
||||
DROP TABLE whatever;
|
||||
-- END OF BASIC USAGE TESTS --
|
||||
CREATE TABLE metrics(time timestamptz, device TEXT, value float);
|
||||
SELECT table_name FROM create_hypertable('metrics','time');
|
||||
NOTICE: adding not-null constraint to column "time"
|
||||
table_name
|
||||
------------
|
||||
metrics
|
||||
(1 row)
|
||||
|
||||
INSERT INTO metrics SELECT generate_series('1999-12-20'::timestamptz,'2000-02-01'::timestamptz,'12 day'::interval), 'dev1', 0.25;
|
||||
SELECT current_setting('timezone');
|
||||
current_setting
|
||||
-----------------
|
||||
PST8PDT
|
||||
(1 row)
|
||||
|
||||
-- should be blocked because non-immutable expression
|
||||
\set ON_ERROR_STOP 0
|
||||
CREATE MATERIALIZED VIEW cagg1 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 day', time, current_setting('timezone')) FROM metrics GROUP BY 1;
|
||||
ERROR: only immutable expressions allowed in time bucket function
|
||||
\set ON_ERROR_STOP 1
|
||||
CREATE MATERIALIZED VIEW cagg1 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 day', time, 'PST8PDT') FROM metrics GROUP BY 1;
|
||||
NOTICE: refreshing continuous aggregate "cagg1"
|
||||
SELECT * FROM cagg1;
|
||||
time_bucket
|
||||
------------------------------
|
||||
Mon Dec 20 00:00:00 1999 PST
|
||||
Sat Jan 01 00:00:00 2000 PST
|
||||
Thu Jan 13 00:00:00 2000 PST
|
||||
Tue Jan 25 00:00:00 2000 PST
|
||||
(4 rows)
|
||||
|
||||
CREATE MATERIALIZED VIEW cagg2 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT') FROM metrics GROUP BY 1;
|
||||
NOTICE: refreshing continuous aggregate "cagg2"
|
||||
SELECT * FROM cagg2;
|
||||
time_bucket
|
||||
------------------------------
|
||||
Wed Dec 01 00:00:00 1999 PST
|
||||
Sat Jan 01 00:00:00 2000 PST
|
||||
(2 rows)
|
||||
|
||||
-- custom origin
|
||||
CREATE MATERIALIZED VIEW cagg3 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT', '2000-01-01'::timestamptz) FROM metrics GROUP BY 1;
|
||||
NOTICE: refreshing continuous aggregate "cagg3"
|
||||
SELECT * FROM cagg3;
|
||||
time_bucket
|
||||
------------------------------
|
||||
Wed Dec 01 00:00:00 1999 PST
|
||||
Sat Jan 01 00:00:00 2000 PST
|
||||
(2 rows)
|
||||
|
||||
-- offset not supported atm
|
||||
\set ON_ERROR_STOP 0
|
||||
CREATE MATERIALIZED VIEW cagg4 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT', "offset":= INTERVAL '15 day') FROM metrics GROUP BY 1;
|
||||
ERROR: continuous aggregate view must include a valid time bucket function
|
||||
\set ON_ERROR_STOP 1
|
||||
|
@ -30,17 +30,6 @@ INSERT INTO conditions (day, city, temperature) VALUES
|
||||
('2021-06-26', 'Moscow', 32),
|
||||
('2021-06-27', 'Moscow', 31);
|
||||
\set ON_ERROR_STOP 0
|
||||
-- Make sure NULL can't be specified as an origin
|
||||
CREATE MATERIALIZED VIEW conditions_summary_weekly
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
SELECT city,
|
||||
timescaledb_experimental.time_bucket_ng('7 days', day, null) AS bucket,
|
||||
MIN(temperature),
|
||||
MAX(temperature)
|
||||
FROM conditions
|
||||
GROUP BY city, bucket
|
||||
WITH NO DATA;
|
||||
ERROR: invalid origin value: null
|
||||
-- Make sure 'infinity' can't be specified as an origin
|
||||
CREATE MATERIALIZED VIEW conditions_summary_weekly
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
@ -52,17 +41,6 @@ FROM conditions
|
||||
GROUP BY city, bucket
|
||||
WITH NO DATA;
|
||||
ERROR: invalid origin value: infinity
|
||||
-- For monthly buckets origin should be the first day of the month
|
||||
CREATE MATERIALIZED VIEW conditions_summary_weekly
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
SELECT city,
|
||||
timescaledb_experimental.time_bucket_ng('1 month', day, '2021-06-03') AS bucket,
|
||||
MIN(temperature),
|
||||
MAX(temperature)
|
||||
FROM conditions
|
||||
GROUP BY city, bucket
|
||||
WITH NO DATA;
|
||||
ERROR: for monthly buckets origin must be the first day of the month
|
||||
-- Make sure buckets like '1 months 15 days" (fixed+variable-sized) are not allowed
|
||||
CREATE MATERIALIZED VIEW conditions_summary_weekly
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
@ -95,24 +73,6 @@ SELECT mat_hypertable_id AS cagg_id, raw_hypertable_id AS ht_id
|
||||
FROM _timescaledb_catalog.continuous_agg
|
||||
WHERE user_view_name = 'conditions_summary_weekly'
|
||||
\gset
|
||||
-- Make sure this is treated as a variable-sized bucket case
|
||||
SELECT bucket_width
|
||||
FROM _timescaledb_catalog.continuous_agg
|
||||
WHERE mat_hypertable_id = :cagg_id;
|
||||
bucket_width
|
||||
--------------
|
||||
-1
|
||||
(1 row)
|
||||
|
||||
-- Make sure the origin is saved in the catalog table
|
||||
SELECT experimental, name, bucket_width, origin, timezone
|
||||
FROM _timescaledb_catalog.continuous_aggs_bucket_function
|
||||
WHERE mat_hypertable_id = :cagg_id;
|
||||
experimental | name | bucket_width | origin | timezone
|
||||
--------------+----------------+--------------+--------------------------+----------
|
||||
t | time_bucket_ng | @ 7 days | Mon Jan 03 00:00:00 2000 |
|
||||
(1 row)
|
||||
|
||||
-- Make sure truncating of the refresh window works
|
||||
\set ON_ERROR_STOP 0
|
||||
CALL refresh_continuous_aggregate('conditions_summary_weekly', '2021-06-14', '2021-06-20');
|
||||
@ -955,7 +915,7 @@ SELECT city,
|
||||
MAX(temperature)
|
||||
FROM conditions_timestamptz
|
||||
GROUP BY city, bucket;
|
||||
ERROR: for monthly buckets origin must be the first day of the month
|
||||
ERROR: origin must be the first day of the month
|
||||
-- Make sure buckets like '1 months 15 days" (fixed+variable-sized) are not allowed
|
||||
CREATE MATERIALIZED VIEW conditions_summary_timestamptz
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
@ -1139,7 +1099,7 @@ SELECT add_continuous_aggregate_policy('conditions_summary_timestamptz',
|
||||
DROP TABLE conditions_timestamptz CASCADE;
|
||||
NOTICE: drop cascades to 3 other objects
|
||||
NOTICE: drop cascades to 3 other objects
|
||||
NOTICE: drop cascades to table _timescaledb_internal._hyper_18_248_chunk
|
||||
NOTICE: drop cascades to table _timescaledb_internal._hyper_19_248_chunk
|
||||
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
||||
DROP DATABASE :DATA_NODE_1;
|
||||
DROP DATABASE :DATA_NODE_2;
|
||||
|
@ -196,6 +196,7 @@ ORDER BY pronamespace::regnamespace::text COLLATE "C", p.oid::regprocedure::text
|
||||
time_bucket(interval,date,interval)
|
||||
time_bucket(interval,timestamp with time zone)
|
||||
time_bucket(interval,timestamp with time zone,interval)
|
||||
time_bucket(interval,timestamp with time zone,text,timestamp with time zone,interval)
|
||||
time_bucket(interval,timestamp with time zone,timestamp with time zone)
|
||||
time_bucket(interval,timestamp without time zone)
|
||||
time_bucket(interval,timestamp without time zone,interval)
|
||||
|
@ -268,3 +268,30 @@ SELECT relname FROM pg_class WHERE oid = :mat_table;
|
||||
DROP TABLE whatever;
|
||||
|
||||
-- END OF BASIC USAGE TESTS --
|
||||
|
||||
CREATE TABLE metrics(time timestamptz, device TEXT, value float);
|
||||
SELECT table_name FROM create_hypertable('metrics','time');
|
||||
INSERT INTO metrics SELECT generate_series('1999-12-20'::timestamptz,'2000-02-01'::timestamptz,'12 day'::interval), 'dev1', 0.25;
|
||||
|
||||
SELECT current_setting('timezone');
|
||||
|
||||
-- should be blocked because non-immutable expression
|
||||
\set ON_ERROR_STOP 0
|
||||
CREATE MATERIALIZED VIEW cagg1 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 day', time, current_setting('timezone')) FROM metrics GROUP BY 1;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
CREATE MATERIALIZED VIEW cagg1 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 day', time, 'PST8PDT') FROM metrics GROUP BY 1;
|
||||
SELECT * FROM cagg1;
|
||||
|
||||
CREATE MATERIALIZED VIEW cagg2 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT') FROM metrics GROUP BY 1;
|
||||
SELECT * FROM cagg2;
|
||||
|
||||
-- custom origin
|
||||
CREATE MATERIALIZED VIEW cagg3 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT', '2000-01-01'::timestamptz) FROM metrics GROUP BY 1;
|
||||
SELECT * FROM cagg3;
|
||||
|
||||
-- offset not supported atm
|
||||
\set ON_ERROR_STOP 0
|
||||
CREATE MATERIALIZED VIEW cagg4 WITH (timescaledb.continuous,timescaledb.materialized_only=true) AS SELECT time_bucket('1 month', time, 'PST8PDT', "offset":= INTERVAL '15 day') FROM metrics GROUP BY 1;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
|
@ -30,17 +30,6 @@ INSERT INTO conditions (day, city, temperature) VALUES
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
|
||||
-- Make sure NULL can't be specified as an origin
|
||||
CREATE MATERIALIZED VIEW conditions_summary_weekly
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
SELECT city,
|
||||
timescaledb_experimental.time_bucket_ng('7 days', day, null) AS bucket,
|
||||
MIN(temperature),
|
||||
MAX(temperature)
|
||||
FROM conditions
|
||||
GROUP BY city, bucket
|
||||
WITH NO DATA;
|
||||
|
||||
-- Make sure 'infinity' can't be specified as an origin
|
||||
CREATE MATERIALIZED VIEW conditions_summary_weekly
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
@ -52,17 +41,6 @@ FROM conditions
|
||||
GROUP BY city, bucket
|
||||
WITH NO DATA;
|
||||
|
||||
-- For monthly buckets origin should be the first day of the month
|
||||
CREATE MATERIALIZED VIEW conditions_summary_weekly
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
SELECT city,
|
||||
timescaledb_experimental.time_bucket_ng('1 month', day, '2021-06-03') AS bucket,
|
||||
MIN(temperature),
|
||||
MAX(temperature)
|
||||
FROM conditions
|
||||
GROUP BY city, bucket
|
||||
WITH NO DATA;
|
||||
|
||||
-- Make sure buckets like '1 months 15 days" (fixed+variable-sized) are not allowed
|
||||
CREATE MATERIALIZED VIEW conditions_summary_weekly
|
||||
WITH (timescaledb.continuous, timescaledb.materialized_only=true) AS
|
||||
@ -95,16 +73,6 @@ FROM _timescaledb_catalog.continuous_agg
|
||||
WHERE user_view_name = 'conditions_summary_weekly'
|
||||
\gset
|
||||
|
||||
-- Make sure this is treated as a variable-sized bucket case
|
||||
SELECT bucket_width
|
||||
FROM _timescaledb_catalog.continuous_agg
|
||||
WHERE mat_hypertable_id = :cagg_id;
|
||||
|
||||
-- Make sure the origin is saved in the catalog table
|
||||
SELECT experimental, name, bucket_width, origin, timezone
|
||||
FROM _timescaledb_catalog.continuous_aggs_bucket_function
|
||||
WHERE mat_hypertable_id = :cagg_id;
|
||||
|
||||
-- Make sure truncating of the refresh window works
|
||||
\set ON_ERROR_STOP 0
|
||||
CALL refresh_continuous_aggregate('conditions_summary_weekly', '2021-06-14', '2021-06-20');
|
||||
|
Loading…
x
Reference in New Issue
Block a user