Fix next_start calculation for fixed schedules

This patch fixes several issues with next_start calculation.

- Previously, the offset was added twice in some cases.
This is fixed by this patch.

- Additionally, schedule intervals with month components
were not handled correctly.
Internally, time_bucket with origin is used to calculate
the next start. However, in the case of month intervals, the
timestamp calculated for a bucket is always aligned on the first
day of the month, regardless of origin.
Therefore, previously the result was aligned with origin by adding
the difference between origin and its respective time bucket.
This difference was computed as a fixed length interval in terms
of days and time. That computation led to incorrect computation of
next start occasionally, for example when a job should be executed on
the last day of a month.
That is fixed by adding an appropriate interval of months to
initial_start and letting Postgres handle this computation properly.

Fixes #5216
This commit is contained in:
Konstantina Skovola 2023-01-27 17:06:59 +02:00 committed by Konstantina Skovola
parent 756ef68d0a
commit 348796f9d9
10 changed files with 1175 additions and 83 deletions

View File

@ -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
* #5290 Compression can't be enabled on continuous aggregates when segmentby/orderby columns need quotation
* #5239 Fix next_start calculation for fixed schedules
## 2.9.3 (2023-02-03)
@ -35,6 +36,7 @@ upgrade as soon as possible.
* @ssmoss for reporting issues on continuous aggregates
* @jaskij for reporting the compliation issue that occurred with clang
## 2.9.2 (2023-01-26)
This release contains bug fixes since the 2.9.1 release.

View File

@ -170,61 +170,105 @@ typedef struct
} JobResultCtx;
/*
* time_bucket(schedule_interval, finish_time, origin => initial_start)
* logic is the following
* Ideally we would return
* time_bucket(schedule_interval, finish_time, origin => initial_start, timezone).
* That is what we return when the schedule interval does not have month components.
* However, when there is a month component in the schedule interval,
* then supplying the origin in time_bucket
* does not work and the returned bucket is aligned on the start of the month.
* In those cases, we only have month components. So we compute the difference in
* months between the initial_start's timebucket and the finish time's bucket.
*/
static TimestampTz
get_next_scheduled_execution_slot(BgwJob *job, TimestampTz finish_time)
{
Assert(job->fd.fixed_schedule == true);
Datum timebucket_fini, result, offset;
Datum schedint_datum = IntervalPGetDatum(&job->fd.schedule_interval);
Datum timebucket_fini, timebucket_init, result;
if (job->fd.timezone == NULL)
Interval one_month = {
.day = 0,
.time = 0,
.month = 1,
};
if (job->fd.schedule_interval.month > 0)
{
offset = DirectFunctionCall2(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(job->fd.initial_start));
if (job->fd.timezone == NULL)
{
timebucket_init = DirectFunctionCall2(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(job->fd.initial_start));
timebucket_fini = DirectFunctionCall2(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time));
}
else
{
char *tz = text_to_cstring(job->fd.timezone);
timebucket_fini = DirectFunctionCall3(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
CStringGetTextDatum(tz));
timebucket_fini = DirectFunctionCall3(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
TimestampTzGetDatum(job->fd.initial_start));
/* always the next time_bucket */
result = DirectFunctionCall2(timestamptz_pl_interval, timebucket_fini, schedint_datum);
timebucket_init = DirectFunctionCall3(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(job->fd.initial_start),
CStringGetTextDatum(tz));
}
/* always the next bucket */
timebucket_fini =
DirectFunctionCall2(timestamptz_pl_interval, timebucket_fini, schedint_datum);
/* get the number of months between them */
Datum year_init =
DirectFunctionCall2(timestamptz_part, CStringGetTextDatum("year"), timebucket_init);
Datum year_fini =
DirectFunctionCall2(timestamptz_part, CStringGetTextDatum("year"), timebucket_fini);
Datum month_init =
DirectFunctionCall2(timestamptz_part, CStringGetTextDatum("month"), timebucket_init);
Datum month_fini =
DirectFunctionCall2(timestamptz_part, CStringGetTextDatum("month"), timebucket_fini);
/* convert everything to months */
float8 month_diff = DatumGetFloat8(year_fini) * 12 + DatumGetFloat8(month_fini) -
(DatumGetFloat8(year_init) * 12 + DatumGetFloat8(month_init));
Datum months_to_add = DirectFunctionCall2(interval_mul,
IntervalPGetDatum(&one_month),
Float8GetDatum(month_diff));
result = DirectFunctionCall2(timestamptz_pl_interval,
TimestampTzGetDatum(job->fd.initial_start),
months_to_add);
}
else
{
char *tz = text_to_cstring(job->fd.timezone);
timebucket_fini = DirectFunctionCall4(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
CStringGetTextDatum(tz),
TimestampTzGetDatum(job->fd.initial_start));
/* always the next time_bucket */
result = DirectFunctionCall2(timestamptz_pl_interval, timebucket_fini, schedint_datum);
offset = DirectFunctionCall3(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(job->fd.initial_start),
CStringGetTextDatum(tz));
if (job->fd.timezone == NULL)
{
/* it is safe to use the origin in time_bucket calculation */
timebucket_fini = DirectFunctionCall3(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
TimestampTzGetDatum(job->fd.initial_start));
result = timebucket_fini;
}
else
{
char *tz = text_to_cstring(job->fd.timezone);
timebucket_fini = DirectFunctionCall4(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
CStringGetTextDatum(tz),
TimestampTzGetDatum(job->fd.initial_start));
result = timebucket_fini;
}
}
offset = DirectFunctionCall2(timestamp_mi, TimestampTzGetDatum(job->fd.initial_start), offset);
/* if we have a month component, the origin doesn't work so we must manually
include the offset */
if (job->fd.schedule_interval.month)
{
result = DirectFunctionCall2(timestamptz_pl_interval, result, offset);
}
/*
* adding the schedule interval above to get the next bucket might still not hit
* the next bucket if we are crossing DST. So we can end up with a next_start value
* that is actually less than the finish time of the job. Hence, we have to make sure
* the next scheduled slot we compute is in the future and not in the past
*/
while (DatumGetTimestampTz(result) <= finish_time)
{
result = DirectFunctionCall2(timestamptz_pl_interval, result, schedint_datum);
}
return DatumGetTimestampTz(result);
}

View File

@ -67,55 +67,89 @@ ts_test_next_scheduled_execution_slot(PG_FUNCTION_ARGS)
TimestampTz initial_start = PG_GETARG_TIMESTAMPTZ(2);
text *timezone = PG_ARGISNULL(3) ? NULL : PG_GETARG_TEXT_PP(3);
Datum timebucket_fini, result, offset;
Datum timebucket_fini, timebucket_init, result;
Datum schedint_datum = IntervalPGetDatum(schedule_interval);
Interval one_month = {
.day = 0,
.time = 0,
.month = 1,
};
if (timezone == NULL)
if (schedule_interval->month > 0)
{
offset = DirectFunctionCall2(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(initial_start));
if (timezone == NULL)
{
timebucket_init = DirectFunctionCall2(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(initial_start));
timebucket_fini = DirectFunctionCall2(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time));
}
else
{
char *tz = text_to_cstring(timezone);
timebucket_fini = DirectFunctionCall3(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
CStringGetTextDatum(tz));
timebucket_fini = DirectFunctionCall3(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
TimestampTzGetDatum(initial_start));
/* always the next time_bucket */
result = DirectFunctionCall2(timestamptz_pl_interval, timebucket_fini, schedint_datum);
timebucket_init = DirectFunctionCall3(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(initial_start),
CStringGetTextDatum(tz));
}
/* always the next bucket */
timebucket_fini =
DirectFunctionCall2(timestamptz_pl_interval, timebucket_fini, schedint_datum);
/* get the number of months between them */
Datum year_init =
DirectFunctionCall2(timestamptz_part, CStringGetTextDatum("year"), timebucket_init);
Datum year_fini =
DirectFunctionCall2(timestamptz_part, CStringGetTextDatum("year"), timebucket_fini);
Datum month_init =
DirectFunctionCall2(timestamptz_part, CStringGetTextDatum("month"), timebucket_init);
Datum month_fini =
DirectFunctionCall2(timestamptz_part, CStringGetTextDatum("month"), timebucket_fini);
/* convert everything to months */
float8 month_diff = DatumGetFloat8(year_fini) * 12 + DatumGetFloat8(month_fini) -
(DatumGetFloat8(year_init) * 12 + DatumGetFloat8(month_init));
Datum months_to_add = DirectFunctionCall2(interval_mul,
IntervalPGetDatum(&one_month),
Float8GetDatum(month_diff));
result = DirectFunctionCall2(timestamptz_pl_interval,
TimestampTzGetDatum(initial_start),
months_to_add);
}
else
{
char *tz = text_to_cstring(timezone);
timebucket_fini = DirectFunctionCall4(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
CStringGetTextDatum(tz),
TimestampTzGetDatum(initial_start));
/* always the next time_bucket */
result = DirectFunctionCall2(timestamptz_pl_interval, timebucket_fini, schedint_datum);
offset = DirectFunctionCall3(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(initial_start),
CStringGetTextDatum(tz));
if (timezone == NULL)
{
/* it is safe to use the origin in time_bucket calculation */
timebucket_fini = DirectFunctionCall3(ts_timestamptz_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
TimestampTzGetDatum(initial_start));
result = timebucket_fini;
}
else
{
char *tz = text_to_cstring(timezone);
timebucket_fini = DirectFunctionCall4(ts_timestamptz_timezone_bucket,
schedint_datum,
TimestampTzGetDatum(finish_time),
CStringGetTextDatum(tz),
TimestampTzGetDatum(initial_start));
result = timebucket_fini;
}
}
offset = DirectFunctionCall2(timestamp_mi, TimestampTzGetDatum(initial_start), offset);
/* if we have a month component, the origin doesn't work so we must manually
include the offset */
if (schedule_interval->month)
{
result = DirectFunctionCall2(timestamptz_pl_interval, result, offset);
}
/*
* adding the schedule interval above to get the next bucket might still not hit
* the next bucket if we are crossing DST. So we can end up with a next_start value
* that is actually less than the finish time of the job. Hence, we have to make sure
* the next scheduled slot we compute is in the future and not in the past
*/
while (DatumGetTimestampTz(result) <= finish_time)
{
result = DirectFunctionCall2(timestamptz_pl_interval, result, schedint_datum);
}
return result;
}

View File

@ -0,0 +1,807 @@
-- 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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
select '2023-01-02 11:53:19.059771+02'::timestamptz as finish_time \gset
select :'finish_time'::timestamptz - interval '5 sec' as start_time \gset
-- years
select ts_test_next_scheduled_execution_slot('1 year', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "1 yr",
ts_test_next_scheduled_execution_slot('1 year', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "1 yr timezone",
ts_test_next_scheduled_execution_slot('10 year', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "10 yr",
ts_test_next_scheduled_execution_slot('10 year', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "10 yr timezone";
1 yr | 1 yr timezone | 10 yr | 10 yr timezone
-------------------------------------+-------------------------------------+-------------------------------------+-------------------------------------
Tue Jan 02 01:53:14.059771 2024 PST | Tue Jan 02 01:53:14.059771 2024 PST | Sun Jan 02 01:53:14.059771 2033 PST | Sun Jan 02 01:53:14.059771 2033 PST
(1 row)
-- weeks
select ts_test_next_scheduled_execution_slot('1 week', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "1 week",
ts_test_next_scheduled_execution_slot('1 week', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "1 week timezone",
ts_test_next_scheduled_execution_slot('2 week', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "2 week",
ts_test_next_scheduled_execution_slot('2 week', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "2 week timezone";
1 week | 1 week timezone | 2 week | 2 week timezone
-------------------------------------+-------------------------------------+-------------------------------------+-------------------------------------
Mon Jan 09 01:53:14.059771 2023 PST | Mon Jan 09 01:53:14.059771 2023 PST | Mon Jan 16 01:53:14.059771 2023 PST | Mon Jan 16 01:53:14.059771 2023 PST
(1 row)
-- months
select ts_test_next_scheduled_execution_slot('1 month', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "1 month",
ts_test_next_scheduled_execution_slot('1 month', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "1 month timezone",
ts_test_next_scheduled_execution_slot('2 month', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "2 month",
ts_test_next_scheduled_execution_slot('2 month', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "2 month timezone";
1 month | 1 month timezone | 2 month | 2 month timezone
-------------------------------------+-------------------------------------+-------------------------------------+-------------------------------------
Thu Feb 02 01:53:14.059771 2023 PST | Thu Feb 02 01:53:14.059771 2023 PST | Thu Mar 02 01:53:14.059771 2023 PST | Thu Mar 02 01:53:14.059771 2023 PST
(1 row)
-- timezone in Berlin changes between 1 and 2 am
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 hour\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 hour\''
\set START_TIME_BEFORE_SUMMER_SWITCH 'TIMESTAMPTZ \'2022-03-27 01:00:00\''
\set FINISH_TIME_AFTER_SUMMER_SWITCH 'TIMESTAMPTZ \'2022-03-27 01:19:20\''
\set START_TIME_BEFORE_WINTER_SWITCH 'TIMESTAMPTZ \'2022-10-30 01:01:00\''
\set FINISH_TIME_AFTER_WINTER_SWITCH 'TIMESTAMPTZ \'2022-10-30 01:10:19\''
\ir include/scheduler_fixed_common.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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
-- test what happens across DST
-- go from +1 to +2
set timezone to 'Europe/Berlin';
-- DST switch on March 27th 2022, in particular, between 1am and 2 am (old time)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_SUMMER_SWITCH as initial_start_summer_switch, :FINISH_TIME_AFTER_SUMMER_SWITCH as first_finish_time_summer_switch;
initial_start_summer_switch | first_finish_time_summer_switch
------------------------------+---------------------------------
Sun Mar 27 01:00:00 2022 CET | Sun Mar 27 01:19:20 2022 CET
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Mar 27 03:00:00 2022 CEST | Sun Mar 27 03:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Mar 27 04:00:00 2022 CEST | Sun Mar 27 04:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Mar 27 05:00:00 2022 CEST | Sun Mar 27 05:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Mar 27 06:00:00 2022 CEST | Sun Mar 27 06:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Mar 27 07:00:00 2022 CEST | Sun Mar 27 07:00:00 2022 CEST
(1 row)
--------- and then +2 to +1
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_WINTER_SWITCH as initial_start_winter_switch, :FINISH_TIME_AFTER_WINTER_SWITCH as first_finish_time_winter_switch;
initial_start_winter_switch | first_finish_time_winter_switch
-------------------------------+---------------------------------
Sun Oct 30 01:01:00 2022 CEST | Sun Oct 30 01:10:19 2022 CEST
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Oct 30 02:01:00 2022 CEST | Sun Oct 30 02:01:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Oct 30 02:01:00 2022 CET | Sun Oct 30 02:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Oct 30 03:01:00 2022 CET | Sun Oct 30 03:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Oct 30 04:01:00 2022 CET | Sun Oct 30 04:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Oct 30 05:00:00 2022 CET | Sun Oct 30 05:00:00 2022 CET
(1 row)
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 day\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 day\''
\ir include/scheduler_fixed_common.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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
-- test what happens across DST
-- go from +1 to +2
set timezone to 'Europe/Berlin';
-- DST switch on March 27th 2022, in particular, between 1am and 2 am (old time)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_SUMMER_SWITCH as initial_start_summer_switch, :FINISH_TIME_AFTER_SUMMER_SWITCH as first_finish_time_summer_switch;
initial_start_summer_switch | first_finish_time_summer_switch
------------------------------+---------------------------------
Sun Mar 27 01:00:00 2022 CET | Sun Mar 27 01:19:20 2022 CET
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Mon Mar 28 01:00:00 2022 CEST | Mon Mar 28 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Tue Mar 29 01:00:00 2022 CEST | Tue Mar 29 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Tue Mar 29 02:00:00 2022 CEST | Wed Mar 30 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Wed Mar 30 02:00:00 2022 CEST | Thu Mar 31 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Thu Mar 31 02:00:00 2022 CEST | Fri Apr 01 01:00:00 2022 CEST
(1 row)
--------- and then +2 to +1
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_WINTER_SWITCH as initial_start_winter_switch, :FINISH_TIME_AFTER_WINTER_SWITCH as first_finish_time_winter_switch;
initial_start_winter_switch | first_finish_time_winter_switch
-------------------------------+---------------------------------
Sun Oct 30 01:01:00 2022 CEST | Sun Oct 30 01:10:19 2022 CEST
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Mon Oct 31 01:01:00 2022 CET | Mon Oct 31 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Tue Nov 01 00:01:00 2022 CET | Tue Nov 01 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Wed Nov 02 00:01:00 2022 CET | Wed Nov 02 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Thu Nov 03 00:01:00 2022 CET | Thu Nov 03 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Thu Nov 03 01:00:00 2022 CET | Fri Nov 04 01:00:00 2022 CET
(1 row)
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 week\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 week\''
\ir include/scheduler_fixed_common.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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
-- test what happens across DST
-- go from +1 to +2
set timezone to 'Europe/Berlin';
-- DST switch on March 27th 2022, in particular, between 1am and 2 am (old time)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_SUMMER_SWITCH as initial_start_summer_switch, :FINISH_TIME_AFTER_SUMMER_SWITCH as first_finish_time_summer_switch;
initial_start_summer_switch | first_finish_time_summer_switch
------------------------------+---------------------------------
Sun Mar 27 01:00:00 2022 CET | Sun Mar 27 01:19:20 2022 CET
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Apr 03 01:00:00 2022 CEST | Sun Apr 03 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Apr 10 01:00:00 2022 CEST | Sun Apr 10 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Apr 10 02:00:00 2022 CEST | Sun Apr 17 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Apr 17 02:00:00 2022 CEST | Sun Apr 24 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Apr 24 02:00:00 2022 CEST | Sun May 01 01:00:00 2022 CEST
(1 row)
--------- and then +2 to +1
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_WINTER_SWITCH as initial_start_winter_switch, :FINISH_TIME_AFTER_WINTER_SWITCH as first_finish_time_winter_switch;
initial_start_winter_switch | first_finish_time_winter_switch
-------------------------------+---------------------------------
Sun Oct 30 01:01:00 2022 CEST | Sun Oct 30 01:10:19 2022 CEST
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Nov 06 01:01:00 2022 CET | Sun Nov 06 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Nov 13 00:01:00 2022 CET | Sun Nov 13 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Nov 20 00:01:00 2022 CET | Sun Nov 20 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Nov 27 00:01:00 2022 CET | Sun Nov 27 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Nov 27 01:00:00 2022 CET | Sun Dec 04 01:00:00 2022 CET
(1 row)
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 month\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 month\''
\ir include/scheduler_fixed_common.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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
-- test what happens across DST
-- go from +1 to +2
set timezone to 'Europe/Berlin';
-- DST switch on March 27th 2022, in particular, between 1am and 2 am (old time)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_SUMMER_SWITCH as initial_start_summer_switch, :FINISH_TIME_AFTER_SUMMER_SWITCH as first_finish_time_summer_switch;
initial_start_summer_switch | first_finish_time_summer_switch
------------------------------+---------------------------------
Sun Mar 27 01:00:00 2022 CET | Sun Mar 27 01:19:20 2022 CET
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Wed Apr 27 01:00:00 2022 CEST | Wed Apr 27 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Fri May 27 01:00:00 2022 CEST | Fri May 27 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Mon Jun 27 01:00:00 2022 CEST | Mon Jun 27 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Wed Jul 27 01:00:00 2022 CEST | Wed Jul 27 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sat Aug 27 01:00:00 2022 CEST | Sat Aug 27 01:00:00 2022 CEST
(1 row)
--------- and then +2 to +1
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_WINTER_SWITCH as initial_start_winter_switch, :FINISH_TIME_AFTER_WINTER_SWITCH as first_finish_time_winter_switch;
initial_start_winter_switch | first_finish_time_winter_switch
-------------------------------+---------------------------------
Sun Oct 30 01:01:00 2022 CEST | Sun Oct 30 01:10:19 2022 CEST
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Wed Nov 30 01:01:00 2022 CET | Wed Nov 30 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Fri Dec 30 01:01:00 2022 CET | Fri Dec 30 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Mon Jan 30 01:01:00 2023 CET | Mon Jan 30 01:01:00 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Tue Feb 28 01:01:00 2023 CET | Tue Feb 28 01:01:00 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Mon Mar 27 01:00:00 2023 CEST | Mon Mar 27 01:00:00 2023 CEST
(1 row)
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 year\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 year\''
\ir include/scheduler_fixed_common.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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
-- test what happens across DST
-- go from +1 to +2
set timezone to 'Europe/Berlin';
-- DST switch on March 27th 2022, in particular, between 1am and 2 am (old time)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_SUMMER_SWITCH as initial_start_summer_switch, :FINISH_TIME_AFTER_SUMMER_SWITCH as first_finish_time_summer_switch;
initial_start_summer_switch | first_finish_time_summer_switch
------------------------------+---------------------------------
Sun Mar 27 01:00:00 2022 CET | Sun Mar 27 01:19:20 2022 CET
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Mon Mar 27 01:00:00 2023 CEST | Mon Mar 27 01:00:00 2023 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Wed Mar 27 01:00:00 2024 CET | Wed Mar 27 01:00:00 2024 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Thu Mar 27 01:00:00 2025 CET | Thu Mar 27 01:00:00 2025 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Fri Mar 27 01:00:00 2026 CET | Fri Mar 27 01:00:00 2026 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sat Mar 27 01:00:00 2027 CET | Sat Mar 27 01:00:00 2027 CET
(1 row)
--------- and then +2 to +1
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_WINTER_SWITCH as initial_start_winter_switch, :FINISH_TIME_AFTER_WINTER_SWITCH as first_finish_time_winter_switch;
initial_start_winter_switch | first_finish_time_winter_switch
-------------------------------+---------------------------------
Sun Oct 30 01:01:00 2022 CEST | Sun Oct 30 01:10:19 2022 CEST
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Mon Oct 30 01:01:00 2023 CET | Mon Oct 30 01:01:00 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Wed Oct 30 01:01:00 2024 CET | Wed Oct 30 01:01:00 2024 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Thu Oct 30 01:01:00 2025 CET | Thu Oct 30 01:01:00 2025 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Fri Oct 30 01:01:00 2026 CET | Fri Oct 30 01:01:00 2026 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sat Mar 27 01:00:00 2027 CET | Sat Mar 27 01:00:00 2027 CET
(1 row)
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'2 month\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'2 month\''
\ir include/scheduler_fixed_common.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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
-- test what happens across DST
-- go from +1 to +2
set timezone to 'Europe/Berlin';
-- DST switch on March 27th 2022, in particular, between 1am and 2 am (old time)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_SUMMER_SWITCH as initial_start_summer_switch, :FINISH_TIME_AFTER_SUMMER_SWITCH as first_finish_time_summer_switch;
initial_start_summer_switch | first_finish_time_summer_switch
------------------------------+---------------------------------
Sun Mar 27 01:00:00 2022 CET | Sun Mar 27 01:19:20 2022 CET
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Fri May 27 01:00:00 2022 CEST | Fri May 27 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Wed Jul 27 01:00:00 2022 CEST | Wed Jul 27 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Tue Sep 27 01:00:00 2022 CEST | Tue Sep 27 01:00:00 2022 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sun Nov 27 01:00:00 2022 CET | Sun Nov 27 01:00:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Fri Jan 27 01:00:00 2023 CET | Fri Jan 27 01:00:00 2023 CET
(1 row)
--------- and then +2 to +1
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_WINTER_SWITCH as initial_start_winter_switch, :FINISH_TIME_AFTER_WINTER_SWITCH as first_finish_time_winter_switch;
initial_start_winter_switch | first_finish_time_winter_switch
-------------------------------+---------------------------------
Sun Oct 30 01:01:00 2022 CEST | Sun Oct 30 01:10:19 2022 CEST
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Fri Dec 30 01:01:00 2022 CET | Fri Dec 30 01:01:00 2022 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Tue Feb 28 01:01:00 2023 CET | Tue Feb 28 01:01:00 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Sun Apr 30 01:01:00 2023 CEST | Sun Apr 30 01:01:00 2023 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Fri Jun 30 01:01:00 2023 CEST | Fri Jun 30 01:01:00 2023 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Thu Jul 27 01:00:00 2023 CEST | Thu Jul 27 01:00:00 2023 CEST
(1 row)
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'2 year\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'2 year\''
\ir include/scheduler_fixed_common.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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
-- test what happens across DST
-- go from +1 to +2
set timezone to 'Europe/Berlin';
-- DST switch on March 27th 2022, in particular, between 1am and 2 am (old time)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_SUMMER_SWITCH as initial_start_summer_switch, :FINISH_TIME_AFTER_SUMMER_SWITCH as first_finish_time_summer_switch;
initial_start_summer_switch | first_finish_time_summer_switch
------------------------------+---------------------------------
Sun Mar 27 01:00:00 2022 CET | Sun Mar 27 01:19:20 2022 CET
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Wed Mar 27 01:00:00 2024 CET | Wed Mar 27 01:00:00 2024 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Fri Mar 27 01:00:00 2026 CET | Fri Mar 27 01:00:00 2026 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
-------------------------------+-------------------------------
Mon Mar 27 01:00:00 2028 CEST | Mon Mar 27 01:00:00 2028 CEST
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Wed Mar 27 01:00:00 2030 CET | Wed Mar 27 01:00:00 2030 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sat Mar 27 01:00:00 2032 CET | Sat Mar 27 01:00:00 2032 CET
(1 row)
--------- and then +2 to +1
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_WINTER_SWITCH as initial_start_winter_switch, :FINISH_TIME_AFTER_WINTER_SWITCH as first_finish_time_winter_switch;
initial_start_winter_switch | first_finish_time_winter_switch
-------------------------------+---------------------------------
Sun Oct 30 01:01:00 2022 CEST | Sun Oct 30 01:10:19 2022 CEST
(1 row)
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Wed Oct 30 01:01:00 2024 CET | Wed Oct 30 01:01:00 2024 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Fri Oct 30 01:01:00 2026 CET | Fri Oct 30 01:01:00 2026 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Mon Oct 30 01:01:00 2028 CET | Mon Oct 30 01:01:00 2028 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Wed Oct 30 01:01:00 2030 CET | Wed Oct 30 01:01:00 2030 CET
(1 row)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
without_timezone | with_timezone
------------------------------+------------------------------
Sat Mar 27 01:00:00 2032 CET | Sat Mar 27 01:00:00 2032 CET
(1 row)

View File

@ -194,3 +194,68 @@ select :'t3' as without_timezone, :'t3_tz' as with_timezone;
-- test some unacceptable values for schedule interval
select add_job('job_5', schedule_interval => interval '1 month 1week', initial_start => :'initial_start'::timestamptz);
ERROR: month intervals cannot have day or time component
\set client_min_messages = DEBUG;
select '2023-01-02 11:53:19.059771+02'::timestamptz as finish_time \gset
-- years
select ts_test_next_scheduled_execution_slot('1 year', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
ts_test_next_scheduled_execution_slot
---------------------------------------
Tue Jan 02 10:53:16.059771 2024 CET
(1 row)
select ts_test_next_scheduled_execution_slot('2 year', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
ts_test_next_scheduled_execution_slot
---------------------------------------
Thu Jan 02 10:53:16.059771 2025 CET
(1 row)
select ts_test_next_scheduled_execution_slot('10 year', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
ts_test_next_scheduled_execution_slot
---------------------------------------
Sun Jan 02 10:53:16.059771 2033 CET
(1 row)
-- weeks
select ts_test_next_scheduled_execution_slot('1 week', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
ts_test_next_scheduled_execution_slot
---------------------------------------
Mon Jan 09 10:53:16.059771 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot('2 week', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
ts_test_next_scheduled_execution_slot
---------------------------------------
Mon Jan 16 10:53:16.059771 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot('2 week', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
ts_test_next_scheduled_execution_slot
---------------------------------------
Mon Jan 16 10:53:16.059771 2023 CET
(1 row)
-- months
select ts_test_next_scheduled_execution_slot('10 month', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
ts_test_next_scheduled_execution_slot
---------------------------------------
Thu Nov 02 10:53:16.059771 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot('10 month', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec', 'Europe/Athens');
ts_test_next_scheduled_execution_slot
---------------------------------------
Thu Nov 02 10:53:16.059771 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot('2 month', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
ts_test_next_scheduled_execution_slot
---------------------------------------
Thu Mar 02 10:53:16.059771 2023 CET
(1 row)
select ts_test_next_scheduled_execution_slot('2 month', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec', 'Europe/Athens');
ts_test_next_scheduled_execution_slot
---------------------------------------
Thu Mar 02 10:53:16.059771 2023 CET
(1 row)

View File

@ -95,7 +95,8 @@ if(CMAKE_BUILD_TYPE MATCHES Debug)
remote_txn.sql
transparent_decompression_queries.sql
tsl_tables.sql
license_tsl.sql)
license_tsl.sql
fixed_schedules.sql)
endif(CMAKE_BUILD_TYPE MATCHES Debug)
if((${PG_VERSION_MAJOR} GREATER_EQUAL "14"))

View File

@ -0,0 +1,64 @@
-- 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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
select '2023-01-02 11:53:19.059771+02'::timestamptz as finish_time \gset
select :'finish_time'::timestamptz - interval '5 sec' as start_time \gset
-- years
select ts_test_next_scheduled_execution_slot('1 year', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "1 yr",
ts_test_next_scheduled_execution_slot('1 year', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "1 yr timezone",
ts_test_next_scheduled_execution_slot('10 year', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "10 yr",
ts_test_next_scheduled_execution_slot('10 year', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "10 yr timezone";
-- weeks
select ts_test_next_scheduled_execution_slot('1 week', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "1 week",
ts_test_next_scheduled_execution_slot('1 week', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "1 week timezone",
ts_test_next_scheduled_execution_slot('2 week', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "2 week",
ts_test_next_scheduled_execution_slot('2 week', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "2 week timezone";
-- months
select ts_test_next_scheduled_execution_slot('1 month', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "1 month",
ts_test_next_scheduled_execution_slot('1 month', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "1 month timezone",
ts_test_next_scheduled_execution_slot('2 month', :'finish_time'::timestamptz, :'start_time'::timestamptz) as "2 month",
ts_test_next_scheduled_execution_slot('2 month', :'finish_time'::timestamptz, :'start_time'::timestamptz, 'Europe/Athens') as "2 month timezone";
-- timezone in Berlin changes between 1 and 2 am
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 hour\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 hour\''
\set START_TIME_BEFORE_SUMMER_SWITCH 'TIMESTAMPTZ \'2022-03-27 01:00:00\''
\set FINISH_TIME_AFTER_SUMMER_SWITCH 'TIMESTAMPTZ \'2022-03-27 01:19:20\''
\set START_TIME_BEFORE_WINTER_SWITCH 'TIMESTAMPTZ \'2022-10-30 01:01:00\''
\set FINISH_TIME_AFTER_WINTER_SWITCH 'TIMESTAMPTZ \'2022-10-30 01:10:19\''
\ir include/scheduler_fixed_common.sql
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 day\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 day\''
\ir include/scheduler_fixed_common.sql
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 week\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 week\''
\ir include/scheduler_fixed_common.sql
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 month\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 month\''
\ir include/scheduler_fixed_common.sql
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'1 year\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'1 year\''
\ir include/scheduler_fixed_common.sql
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'2 month\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'2 month\''
\ir include/scheduler_fixed_common.sql
\set BUCKET_WIDTH_WINTER_SUMMER 'INTERVAL \'2 year\''
\set BUCKET_WIDTH_SUMMER_WINTER 'INTERVAL \'2 year\''
\ir include/scheduler_fixed_common.sql

View File

@ -0,0 +1,51 @@
-- 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.
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_test_next_scheduled_execution_slot(schedule_interval INTERVAL, finish_time TIMESTAMPTZ, initial_start TIMESTAMPTZ, timezone TEXT = NULL)
RETURNS TIMESTAMPTZ AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set client_min_messages = DEBUG;
-- test what happens across DST
-- go from +1 to +2
set timezone to 'Europe/Berlin';
-- DST switch on March 27th 2022, in particular, between 1am and 2 am (old time)
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :FINISH_TIME_AFTER_SUMMER_SWITCH, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_SUMMER_SWITCH as initial_start_summer_switch, :FINISH_TIME_AFTER_SUMMER_SWITCH as first_finish_time_summer_switch;
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;
--------- and then +2 to +1
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH) as t1 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :FINISH_TIME_AFTER_WINTER_SWITCH, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin')
as t1_tz \gset
select :START_TIME_BEFORE_WINTER_SWITCH as initial_start_winter_switch, :FINISH_TIME_AFTER_WINTER_SWITCH as first_finish_time_winter_switch;
select :'t1' as without_timezone, :'t1_tz' as with_timezone;
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t2 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t1_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t2_tz \gset
select :'t2' as without_timezone, :'t2_tz' as with_timezone;
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t3 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t2_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, timezone => 'Europe/Berlin') as t3_tz \gset
select :'t3' as without_timezone, :'t3_tz' as with_timezone;
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH) as t4 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_SUMMER_WINTER, :'t3_tz'::timestamptz, :START_TIME_BEFORE_WINTER_SWITCH, 'Europe/Berlin') as t4_tz \gset
select :'t4' as without_timezone, :'t4_tz' as with_timezone;
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH) as t5 \gset
select ts_test_next_scheduled_execution_slot(:BUCKET_WIDTH_WINTER_SUMMER, :'t4_tz'::timestamptz, :START_TIME_BEFORE_SUMMER_SWITCH, 'Europe/Berlin') as t5_tz \gset
select :'t5' as without_timezone, :'t5_tz' as with_timezone;

View File

@ -122,3 +122,21 @@ select :'t3' as without_timezone, :'t3_tz' as with_timezone;
\set ON_ERROR_STOP 0
-- test some unacceptable values for schedule interval
select add_job('job_5', schedule_interval => interval '1 month 1week', initial_start => :'initial_start'::timestamptz);
\set client_min_messages = DEBUG;
select '2023-01-02 11:53:19.059771+02'::timestamptz as finish_time \gset
-- years
select ts_test_next_scheduled_execution_slot('1 year', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
select ts_test_next_scheduled_execution_slot('2 year', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
select ts_test_next_scheduled_execution_slot('10 year', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
-- weeks
select ts_test_next_scheduled_execution_slot('1 week', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
select ts_test_next_scheduled_execution_slot('2 week', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
select ts_test_next_scheduled_execution_slot('2 week', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
-- months
select ts_test_next_scheduled_execution_slot('10 month', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
select ts_test_next_scheduled_execution_slot('10 month', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec', 'Europe/Athens');
select ts_test_next_scheduled_execution_slot('2 month', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec');
select ts_test_next_scheduled_execution_slot('2 month', :'finish_time'::timestamptz, :'finish_time'::timestamptz - interval '3 sec', 'Europe/Athens');

View File

@ -5,7 +5,7 @@
use strict;
use warnings;
use TimescaleNode;
use Test::More tests => 4;
use Test::More tests => 5;
# This test checks that a job crash is reported in the job errors table
# (that is, a record gets inserted into the table _timescaledb_internal.job_errors).
@ -40,14 +40,20 @@ my $query_add =
my $jobid = $node->safe_psql('postgres', "$query_add");
is($jobid, '1000', 'job was added');
# sleep 10 to make sure job has started running
$node->safe_psql('postgres', "select pg_sleep(10)");
my $query_pid_exists = <<"END_OF_QUERY";
select count(*) from pg_stat_activity
where application_name like 'User-Defined Action%'
and query like '%custom_proc_sleep60%'
END_OF_QUERY
# select the pid of this job in order to kill it
my $query_pid = <<"END_OF_QUERY";
select pid from pg_stat_activity
where application_name like 'User-Defined Action%'
and query like '%custom_proc_sleep60%'
END_OF_QUERY
ok($node->poll_query_until('postgres', "$query_pid_exists", '1'));
my $pid = $node->safe_psql('postgres', "$query_pid");
isnt($pid, "", "check the pid is not null");
# now kill the one backend