diff --git a/sql/job_api.sql b/sql/job_api.sql index 7f7feb9c5..bf3c9a41b 100644 --- a/sql/job_api.sql +++ b/sql/job_api.sql @@ -24,9 +24,10 @@ CREATE OR REPLACE FUNCTION @extschema@.alter_job( scheduled BOOL = NULL, config JSONB = NULL, next_start TIMESTAMPTZ = NULL, - if_exists BOOL = FALSE + if_exists BOOL = FALSE, + check_config REGPROC = NULL ) -RETURNS TABLE (job_id INTEGER, schedule_interval INTERVAL, max_runtime INTERVAL, max_retries INTEGER, retry_period INTERVAL, scheduled BOOL, config JSONB, next_start TIMESTAMPTZ) +RETURNS TABLE (job_id INTEGER, schedule_interval INTERVAL, max_runtime INTERVAL, max_retries INTEGER, retry_period INTERVAL, scheduled BOOL, config JSONB, next_start TIMESTAMPTZ, check_config TEXT) AS '@MODULE_PATHNAME@', 'ts_job_alter' LANGUAGE C VOLATILE; diff --git a/sql/policy_internal.sql b/sql/policy_internal.sql index b84aae31a..d938c0053 100644 --- a/sql/policy_internal.sql +++ b/sql/policy_internal.sql @@ -6,7 +6,7 @@ CREATE OR REPLACE PROCEDURE _timescaledb_internal.policy_retention(job_id INTEGE AS '@MODULE_PATHNAME@', 'ts_policy_retention_proc' LANGUAGE C; -CREATE OR REPLACE FUNCTION _timescaledb_internal.policy_retention_check(job_id INTEGER, config JSONB) +CREATE OR REPLACE FUNCTION _timescaledb_internal.policy_retention_check(config JSONB) RETURNS void AS '@MODULE_PATHNAME@', 'ts_policy_retention_check' LANGUAGE C; @@ -14,7 +14,7 @@ CREATE OR REPLACE PROCEDURE _timescaledb_internal.policy_reorder(job_id INTEGER, AS '@MODULE_PATHNAME@', 'ts_policy_reorder_proc' LANGUAGE C; -CREATE OR REPLACE FUNCTION _timescaledb_internal.policy_reorder_check(job_id INTEGER, config JSONB) +CREATE OR REPLACE FUNCTION _timescaledb_internal.policy_reorder_check(config JSONB) RETURNS void AS '@MODULE_PATHNAME@', 'ts_policy_reorder_check' LANGUAGE C; @@ -22,7 +22,7 @@ CREATE OR REPLACE PROCEDURE _timescaledb_internal.policy_recompression(job_id IN AS '@MODULE_PATHNAME@', 'ts_policy_recompression_proc' LANGUAGE C; -CREATE OR REPLACE FUNCTION _timescaledb_internal.policy_compression_check(job_id INTEGER, config JSONB) +CREATE OR REPLACE FUNCTION _timescaledb_internal.policy_compression_check(config JSONB) RETURNS void AS '@MODULE_PATHNAME@', 'ts_policy_compression_check' LANGUAGE C; @@ -30,7 +30,7 @@ CREATE OR REPLACE PROCEDURE _timescaledb_internal.policy_refresh_continuous_aggr AS '@MODULE_PATHNAME@', 'ts_policy_refresh_cagg_proc' LANGUAGE C; -CREATE OR REPLACE FUNCTION _timescaledb_internal.policy_refresh_continuous_aggregate_check(job_id INTEGER, config JSONB) +CREATE OR REPLACE FUNCTION _timescaledb_internal.policy_refresh_continuous_aggregate_check(config JSONB) RETURNS void AS '@MODULE_PATHNAME@', 'ts_policy_refresh_cagg_check' LANGUAGE C; diff --git a/sql/updates/latest-dev.sql b/sql/updates/latest-dev.sql index 1f58810e6..3afaa00c4 100644 --- a/sql/updates/latest-dev.sql +++ b/sql/updates/latest-dev.sql @@ -22,11 +22,8 @@ ALTER TABLE _timescaledb_catalog.chunk CREATE INDEX chunk_osm_chunk_idx ON _timescaledb_catalog.chunk (osm_chunk, hypertable_id); -DROP FUNCTION IF EXISTS @extschema@.add_job; -DROP FUNCTION IF EXISTS _timescaledb_internal.policy_retention_check(INTEGER, JSONB); -DROP FUNCTION IF EXISTS _timescaledb_internal.policy_compression_check(INTEGER, JSONB); -DROP FUNCTION IF EXISTS _timescaledb_internal.policy_reorder_check(INTEGER, JSONB); -DROP FUNCTION IF EXISTS _timescaledb_internal.policy_refresh_continuous_aggregate_check(INTEGER, JSONB); +DROP FUNCTION IF EXISTS @extschema@.add_job(REGPROC, INTERVAL, JSONB, TIMESTAMPTZ, BOOL); +DROP FUNCTION IF EXISTS @extschema@.alter_job(INTEGER, INTERVAL, INTERVAL, INTEGER, INTERVAL, BOOL, JSONB, TIMESTAMPTZ, BOOL); -- add fields for check function ALTER TABLE _timescaledb_config.bgw_job @@ -71,3 +68,5 @@ SET check_name = 'policy_refresh_continuous_aggregate_check' WHERE proc_schema = '_timescaledb_internal' AND proc_name = 'policy_refresh_continuous_aggregate'; + +DROP VIEW IF EXISTS timescaledb_information.jobs; diff --git a/sql/updates/reverse-dev.sql b/sql/updates/reverse-dev.sql index 16204fb53..f0824418b 100644 --- a/sql/updates/reverse-dev.sql +++ b/sql/updates/reverse-dev.sql @@ -176,7 +176,7 @@ DROP TABLE _timescaledb_config.bgw_job; CREATE SEQUENCE _timescaledb_config.bgw_job_id_seq MINVALUE 1000; SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_job_id_seq', ''); -SELECT setval('_timescaledb_config.bgw_job_id_seq', last_value, is_called) +SELECT pg_catalog.setval('_timescaledb_config.bgw_job_id_seq', last_value, is_called) FROM _timescaledb_internal.tmp_bgw_job_seq_value; DROP TABLE _timescaledb_internal.tmp_bgw_job_seq_value; @@ -218,8 +218,31 @@ ALTER TABLE _timescaledb_internal.bgw_policy_chunk_stats FOREIGN KEY(job_id) REFERENCES _timescaledb_config.bgw_job(id) ON DELETE CASCADE; -DROP FUNCTION IF EXISTS @extschema@.add_job; +DROP FUNCTION IF EXISTS @extschema@.add_job(REGPROC, INTERVAL, JSONB, TIMESTAMPTZ, BOOL, REGPROC); DROP FUNCTION IF EXISTS _timescaledb_internal.policy_retention_check(JSONB); DROP FUNCTION IF EXISTS _timescaledb_internal.policy_compression_check(JSONB); DROP FUNCTION IF EXISTS _timescaledb_internal.policy_reorder_check(JSONB); DROP FUNCTION IF EXISTS _timescaledb_internal.policy_refresh_continuous_aggregate_check(JSONB); +DROP FUNCTION IF EXISTS @extschema@.alter_job(INTEGER, INTERVAL, INTERVAL, INTEGER, INTERVAL, BOOL, JSONB, TIMESTAMPTZ, BOOL, REGPROC); + +CREATE FUNCTION @extschema@.add_job(proc REGPROC, + schedule_interval INTERVAL, + config JSONB = NULL, + initial_start TIMESTAMPTZ = NULL, + scheduled BOOL = true) +RETURNS INTEGER AS '@MODULE_PATHNAME@', 'ts_job_add' LANGUAGE C VOLATILE; + +CREATE FUNCTION @extschema@.alter_job( + job_id INTEGER, + schedule_interval INTERVAL = NULL, + max_runtime INTERVAL = NULL, + max_retries INTEGER = NULL, + retry_period INTERVAL = NULL, + scheduled BOOL = NULL, + config JSONB = NULL, + next_start TIMESTAMPTZ = NULL, + if_exists BOOL = FALSE +) +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; diff --git a/sql/views.sql b/sql/views.sql index 4c811a1dc..1bd2d975d 100644 --- a/sql/views.sql +++ b/sql/views.sql @@ -98,7 +98,9 @@ SELECT j.id AS job_id, j.config, js.next_start, ht.schema_name AS hypertable_schema, - ht.table_name AS hypertable_name + ht.table_name AS hypertable_name, + j.check_schema, + j.check_name FROM _timescaledb_config.bgw_job j LEFT JOIN _timescaledb_catalog.hypertable ht ON ht.id = j.hypertable_id LEFT JOIN _timescaledb_internal.bgw_job_stat js ON js.job_id = j.id; diff --git a/src/bgw/job.c b/src/bgw/job.c index 397accab7..f2ca69ace 100644 --- a/src/bgw/job.c +++ b/src/bgw/job.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -109,16 +110,21 @@ job_execute_procedure(FuncExpr *funcexpr) void ts_bgw_job_run_config_check(Oid check, int32 job_id, Jsonb *config) { - List *args = - list_make2(makeConst(INT4OID, -1, InvalidOid, 4, Int32GetDatum(job_id), false, true), - makeConst(JSONBOID, -1, InvalidOid, -1, JsonbPGetDatum(config), false, false)); - FuncExpr *funcexpr = - makeFuncExpr(check, VOIDOID, args, InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL); - /* Nothing to check if there is no check function provided */ if (!OidIsValid(check)) return; + /* NULL config may be valid */ + Const *arg; + if (config == NULL) + arg = makeNullConst(JSONBOID, -1, InvalidOid); + else + arg = makeConst(JSONBOID, -1, InvalidOid, -1, JsonbPGetDatum(config), false, false); + + List *args = list_make1(arg); + FuncExpr *funcexpr = + makeFuncExpr(check, VOIDOID, args, InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL); + switch (get_func_prokind(check)) { case PROKIND_FUNCTION: @@ -140,10 +146,8 @@ ts_bgw_job_run_config_check(Oid check, int32 job_id, Jsonb *config) static void job_config_check(BgwJob *job, Jsonb *config) { - const Oid proc_args[] = { INT4OID, JSONBOID }; - List *name; Oid proc; - bool started = false; + ObjectWithArgs *object; /* Both should either be empty or contain a schema and name */ Assert((strlen(NameStr(job->fd.check_schema)) == 0) == @@ -153,27 +157,25 @@ job_config_check(BgwJob *job, Jsonb *config) if (strlen(NameStr(job->fd.check_name)) == 0) return; - if (!IsTransactionOrTransactionBlock()) - { - started = true; - StartTransactionCommand(); - /* executing sql functions requires snapshot */ - PushActiveSnapshot(GetTransactionSnapshot()); - } + object = makeNode(ObjectWithArgs); - /* We are using LookupFuncName here, which will find functions but not - * procedures. We could use LookupFuncWithArgs here instead. */ - name = list_make2(makeString(NameStr(job->fd.check_schema)), - makeString(NameStr(job->fd.check_name))); - proc = LookupFuncName(name, 2, proc_args, false); - ts_bgw_job_run_config_check(proc, job->fd.id, config); - if (started) - { - /* if job does its own transaction handling it might not have set a snapshot */ - if (ActiveSnapshotSet()) - PopActiveSnapshot(); - CommitTransactionCommand(); - } + object->objname = list_make2(makeString(NameStr(job->fd.check_schema)), + makeString(NameStr(job->fd.check_name))); + object->objargs = list_make1(SystemTypeName("jsonb")); + proc = LookupFuncWithArgs(OBJECT_ROUTINE, object, true); + + /* a check function has been registered but it can't be found anymore + because it was dropped or renamed. Allow alter_job to run if that's the case + without validating the config but also print a warning */ + if (OidIsValid(proc)) + ts_bgw_job_run_config_check(proc, job->fd.id, config); + else + elog(WARNING, + "function or procedure %s.%s(config jsonb) not found, skipping config validation for " + "job %d", + NameStr(job->fd.check_schema), + NameStr(job->fd.check_name), + job->fd.id); } static BgwJob * @@ -782,9 +784,24 @@ bgw_job_tuple_update_by_id(TupleInfo *ti, void *const data) repl[AttrNumberGetAttrOffset(Anum_bgw_job_scheduled)] = true; repl[AttrNumberGetAttrOffset(Anum_bgw_job_config)] = true; + + values[AttrNumberGetAttrOffset(Anum_bgw_job_check_schema)] = + NameGetDatum(&updated_job->fd.check_schema); + repl[AttrNumberGetAttrOffset(Anum_bgw_job_check_schema)] = true; + + values[AttrNumberGetAttrOffset(Anum_bgw_job_check_name)] = + NameGetDatum(&updated_job->fd.check_name); + repl[AttrNumberGetAttrOffset(Anum_bgw_job_check_name)] = true; + + if (strlen(NameStr(updated_job->fd.check_name)) == 0) + { + isnull[AttrNumberGetAttrOffset(Anum_bgw_job_check_name)] = true; + isnull[AttrNumberGetAttrOffset(Anum_bgw_job_check_schema)] = true; + } + if (updated_job->fd.config) { - job_config_check(bgw_job_from_tupleinfo(ti, sizeof(BgwJob)), updated_job->fd.config); + job_config_check(updated_job, updated_job->fd.config); values[AttrNumberGetAttrOffset(Anum_bgw_job_config)] = JsonbPGetDatum(updated_job->fd.config); } diff --git a/src/bgw_policy/policy.c b/src/bgw_policy/policy.c index 4f15d84e5..942b641d2 100644 --- a/src/bgw_policy/policy.c +++ b/src/bgw_policy/policy.c @@ -6,10 +6,8 @@ #include #include -#include #include "bgw/job.h" -#include "dimension.h" #include "policy.h" void diff --git a/tsl/src/bgw_policy/compression_api.c b/tsl/src/bgw_policy/compression_api.c index c27452972..a571ad99c 100644 --- a/tsl/src/bgw_policy/compression_api.c +++ b/tsl/src/bgw_policy/compression_api.c @@ -158,11 +158,8 @@ validate_compress_after_type(Oid partitioning_type, Oid compress_after_type) Datum policy_compression_check(PG_FUNCTION_ARGS) { - if (PG_NARGS() != 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1)) - PG_RETURN_VOID(); - PolicyCompressionData policy_data; - policy_compression_read_and_validate_config(PG_GETARG_JSONB_P(1), &policy_data); + policy_compression_read_and_validate_config(PG_GETARG_JSONB_P(0), &policy_data); ts_cache_release(policy_data.hcache); PG_RETURN_VOID(); diff --git a/tsl/src/bgw_policy/continuous_aggregate_api.c b/tsl/src/bgw_policy/continuous_aggregate_api.c index a880f6b81..1930cc5b7 100644 --- a/tsl/src/bgw_policy/continuous_aggregate_api.c +++ b/tsl/src/bgw_policy/continuous_aggregate_api.c @@ -220,10 +220,7 @@ policy_refresh_cagg_proc(PG_FUNCTION_ARGS) Datum policy_refresh_cagg_check(PG_FUNCTION_ARGS) { - if (PG_NARGS() != 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1)) - PG_RETURN_VOID(); - - policy_refresh_cagg_read_and_validate_config(PG_GETARG_JSONB_P(1), NULL); + policy_refresh_cagg_read_and_validate_config(PG_GETARG_JSONB_P(0), NULL); PG_RETURN_VOID(); } diff --git a/tsl/src/bgw_policy/job_api.c b/tsl/src/bgw_policy/job_api.c index ed7947b81..3ecb797db 100644 --- a/tsl/src/bgw_policy/job_api.c +++ b/tsl/src/bgw_policy/job_api.c @@ -10,6 +10,9 @@ #include #include +#include +#include + #include #include @@ -25,7 +28,37 @@ /* Default retry period for reorder_jobs is currently 5 minutes */ #define DEFAULT_RETRY_PERIOD (5 * USECS_PER_MINUTE) -#define ALTER_JOB_NUM_COLS 8 +#define ALTER_JOB_NUM_COLS 9 + +/* + * This function ensures that the check function has the required signature + * @param check A valid Oid + */ +static inline void +validate_check_signature(Oid check) +{ + Oid proc = InvalidOid; + ObjectWithArgs *object; + NameData check_name = { 0 }; + NameData check_schema = { 0 }; + + namestrcpy(&check_schema, get_namespace_name(get_func_namespace(check))); + namestrcpy(&check_name, get_func_name(check)); + + object = makeNode(ObjectWithArgs); + object->objname = + list_make2(makeString(NameStr(check_schema)), makeString(NameStr(check_name))); + object->objargs = list_make1(SystemTypeName("jsonb")); + proc = LookupFuncWithArgs(OBJECT_ROUTINE, object, true); + + if (!OidIsValid(proc)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("function or procedure %s.%s(config jsonb) not found", + NameStr(check_schema), + NameStr(check_name)), + errhint("The check function's signature must be (config jsonb)."))); +} /* * CREATE FUNCTION add_job( @@ -110,8 +143,11 @@ job_add(PG_FUNCTION_ARGS) namestrcpy(&proc_name, func_name); namestrcpy(&owner_name, GetUserNameFromId(owner, false)); - if (config) - ts_bgw_job_run_config_check(check, 0, config); + /* The check exists but may not have the expected signature: (config jsonb) */ + if (OidIsValid(check)) + validate_check_signature(check); + + ts_bgw_job_run_config_check(check, 0, config); job_id = ts_bgw_job_insert_relation(&application_name, schedule_interval, @@ -206,6 +242,7 @@ job_run(PG_FUNCTION_ARGS) * 6 config JSONB = NULL, * 7 next_start TIMESTAMPTZ = NULL * 8 if_exists BOOL = FALSE, + * 9 check_config REGPROC = NULL * ) RETURNS TABLE ( * job_id INTEGER, * schedule_interval INTERVAL, @@ -215,6 +252,7 @@ job_run(PG_FUNCTION_ARGS) * scheduled BOOL, * config JSONB, * next_start TIMESTAMPTZ + * check_config TEXT * ) */ Datum @@ -229,6 +267,13 @@ job_alter(PG_FUNCTION_ARGS) int job_id = PG_GETARG_INT32(0); bool if_exists = PG_GETARG_BOOL(8); BgwJob *job; + NameData check_name = { 0 }; + NameData check_schema = { 0 }; + Oid check = PG_ARGISNULL(9) ? InvalidOid : PG_GETARG_OID(9); + char *check_name_str = NULL; + /* Added space for period and NULL */ + char schema_qualified_check_name[2 * NAMEDATALEN + 2] = { 0 }; + bool unregister_check = (!PG_ARGISNULL(9) && !OidIsValid(check)); TS_PREVENT_FUNC_IF_READ_ONLY(); @@ -259,6 +304,50 @@ job_alter(PG_FUNCTION_ARGS) if (!PG_ARGISNULL(6)) job->fd.config = PG_GETARG_JSONB_P(6); + if (!PG_ARGISNULL(9)) + { + if (OidIsValid(check)) + { + check_name_str = get_func_name(check); + if (check_name_str == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("function with OID %d does not exist", check))); + + if (pg_proc_aclcheck(check, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for function \"%s\"", check_name_str), + errhint("Job owner must have EXECUTE privilege on the function."))); + + namestrcpy(&check_schema, get_namespace_name(get_func_namespace(check))); + namestrcpy(&check_name, check_name_str); + + /* The check exists but may not have the expected signature: (config jsonb) */ + validate_check_signature(check); + + namestrcpy(&job->fd.check_schema, NameStr(check_schema)); + namestrcpy(&job->fd.check_name, NameStr(check_name)); + snprintf(schema_qualified_check_name, + sizeof(schema_qualified_check_name) / sizeof(schema_qualified_check_name[0]), + "%s.%s", + NameStr(check_schema), + check_name_str); + } + } + else + snprintf(schema_qualified_check_name, + sizeof(schema_qualified_check_name) / sizeof(schema_qualified_check_name[0]), + "%s.%s", + NameStr(job->fd.check_schema), + NameStr(job->fd.check_name)); + + if (unregister_check) + { + NameData empty_namedata = { 0 }; + namestrcpy(&job->fd.check_schema, NameStr(empty_namedata)); + namestrcpy(&job->fd.check_name, NameStr(empty_namedata)); + } ts_bgw_job_update_by_id(job_id, job); if (!PG_ARGISNULL(7)) @@ -285,6 +374,13 @@ job_alter(PG_FUNCTION_ARGS) values[7] = TimestampTzGetDatum(next_start); + if (unregister_check) + nulls[8] = true; + else if (strlen(NameStr(job->fd.check_schema)) > 0) + values[8] = CStringGetTextDatum(schema_qualified_check_name); + else + nulls[8] = true; + tuple = heap_form_tuple(tupdesc, values, nulls); return HeapTupleGetDatum(tuple); } diff --git a/tsl/src/bgw_policy/reorder_api.c b/tsl/src/bgw_policy/reorder_api.c index a77e46ed1..1f7cccc32 100644 --- a/tsl/src/bgw_policy/reorder_api.c +++ b/tsl/src/bgw_policy/reorder_api.c @@ -109,12 +109,9 @@ check_valid_index(Hypertable *ht, Name index_name) Datum policy_reorder_check(PG_FUNCTION_ARGS) { - if (PG_NARGS() != 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1)) - PG_RETURN_VOID(); - TS_PREVENT_FUNC_IF_READ_ONLY(); - policy_reorder_read_and_validate_config(PG_GETARG_JSONB_P(1), NULL); + policy_reorder_read_and_validate_config(PG_GETARG_JSONB_P(0), NULL); PG_RETURN_VOID(); } diff --git a/tsl/src/bgw_policy/retention_api.c b/tsl/src/bgw_policy/retention_api.c index 12faf427e..e9d30cdab 100644 --- a/tsl/src/bgw_policy/retention_api.c +++ b/tsl/src/bgw_policy/retention_api.c @@ -43,12 +43,9 @@ policy_retention_proc(PG_FUNCTION_ARGS) Datum policy_retention_check(PG_FUNCTION_ARGS) { - if (PG_NARGS() != 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1)) - PG_RETURN_VOID(); - TS_PREVENT_FUNC_IF_READ_ONLY(); - policy_retention_read_and_validate_config(PG_GETARG_JSONB_P(1), NULL); + policy_retention_read_and_validate_config(PG_GETARG_JSONB_P(0), NULL); PG_RETURN_VOID(); } diff --git a/tsl/test/expected/bgw_custom.out b/tsl/test/expected/bgw_custom.out index 0f1611753..ec1a86ab9 100644 --- a/tsl/test/expected/bgw_custom.out +++ b/tsl/test/expected/bgw_custom.out @@ -74,13 +74,13 @@ SELECT add_job('custom_func_definer', '1h', config:='{"type":"function"}'::jsonb (1 row) SELECT * FROM timescaledb_information.jobs WHERE job_id != 1 ORDER BY 1; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+----------------------------+-------------------+-------------+-------------+--------------+-------------+---------------------+-------------------+-----------+-----------------------+------------+-------------------+----------------- - 1000 | User-Defined Action [1000] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_func | default_perm_user | t | {"type": "function"} | | | - 1001 | User-Defined Action [1001] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_proc | default_perm_user | t | {"type": "procedure"} | | | - 1002 | User-Defined Action [1002] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_proc2 | default_perm_user | t | {"type": "procedure"} | | | - 1003 | User-Defined Action [1003] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_func | default_perm_user | t | {"type": "function"} | | | - 1004 | User-Defined Action [1004] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_func_definer | default_perm_user | t | {"type": "function"} | | | + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+----------------------------+-------------------+-------------+-------------+--------------+-------------+---------------------+-------------------+-----------+-----------------------+------------+-------------------+-----------------+--------------+------------ + 1000 | User-Defined Action [1000] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_func | default_perm_user | t | {"type": "function"} | | | | | + 1001 | User-Defined Action [1001] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_proc | default_perm_user | t | {"type": "procedure"} | | | | | + 1002 | User-Defined Action [1002] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_proc2 | default_perm_user | t | {"type": "procedure"} | | | | | + 1003 | User-Defined Action [1003] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_func | default_perm_user | t | {"type": "function"} | | | | | + 1004 | User-Defined Action [1004] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_func_definer | default_perm_user | t | {"type": "function"} | | | | | (5 rows) SELECT count(*) FROM _timescaledb_config.bgw_job WHERE config->>'type' IN ('procedure', 'function'); @@ -574,3 +574,328 @@ SELECT _timescaledb_internal.stop_background_workers(); t (1 row) +SELECT _timescaledb_internal.restart_background_workers(); + restart_background_workers +---------------------------- + t +(1 row) + +\set ON_ERROR_STOP 0 +-- add test for custom jobs with custom check functions +-- create the functions/procedures to be used as checking functions +CREATE OR REPLACE PROCEDURE test_config_check_proc(config jsonb) +LANGUAGE PLPGSQL +AS $$ +DECLARE + drop_after interval; +BEGIN + SELECT jsonb_object_field_text (config, 'drop_after')::interval INTO STRICT drop_after; + IF drop_after IS NULL THEN + RAISE EXCEPTION 'Config must be not NULL and have drop_after'; + END IF ; +END +$$; +CREATE OR REPLACE FUNCTION test_config_check_func(config jsonb) RETURNS VOID +AS $$ +DECLARE + drop_after interval; +BEGIN + IF config IS NULL THEN + RETURN; + END IF; + SELECT jsonb_object_field_text (config, 'drop_after')::interval INTO STRICT drop_after; + IF drop_after IS NULL THEN + RAISE EXCEPTION 'Config can be NULL but must have drop_after if not'; + END IF ; +END +$$ LANGUAGE PLPGSQL; +-- step 2, create a procedure to run as a custom job +CREATE OR REPLACE PROCEDURE test_proc_with_check(job_id int, config jsonb) +LANGUAGE PLPGSQL +AS $$ +BEGIN + RAISE NOTICE 'Will only print this if config passes checks, my config is %', config; +END +$$; +-- step 3, add the job with the config check function passed as argument +-- test procedures +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_proc'::regproc); +ERROR: Config must be not NULL and have drop_after +select add_job('test_proc_with_check', '5 secs', config => NULL, check_config => 'test_config_check_proc'::regproc); +ERROR: Config must be not NULL and have drop_after +select add_job('test_proc_with_check', '5 secs', config => '{"drop_after": "chicken"}', check_config => 'test_config_check_proc'::regproc); +ERROR: invalid input syntax for type interval: "chicken" +select add_job('test_proc_with_check', '5 secs', config => '{"drop_after": "2 weeks"}', check_config => 'test_config_check_proc'::regproc) +as job_with_proc_check_id \gset +-- test functions +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func'::regproc); +ERROR: Config can be NULL but must have drop_after if not +select add_job('test_proc_with_check', '5 secs', config => NULL, check_config => 'test_config_check_func'::regproc); + add_job +--------- + 1006 +(1 row) + +select add_job('test_proc_with_check', '5 secs', config => '{"drop_after": "chicken"}', check_config => 'test_config_check_func'::regproc); +ERROR: invalid input syntax for type interval: "chicken" +select add_job('test_proc_with_check', '5 secs', config => '{"drop_after": "2 weeks"}', check_config => 'test_config_check_func'::regproc) +as job_with_func_check_id \gset +--- test alter_job +select alter_job(:job_with_func_check_id, config => '{"drop_after":"chicken"}'); +ERROR: invalid input syntax for type interval: "chicken" +select alter_job(:job_with_func_check_id, config => '{"drop_after":"5 years"}'); + alter_job +----------------------------------------------------------------------------------------------------------------- + (1007,"@ 5 secs","@ 0",-1,"@ 5 mins",t,"{""drop_after"": ""5 years""}",-infinity,public.test_config_check_func) +(1 row) + +select alter_job(:job_with_proc_check_id, config => '{"drop_after":"4 days"}'); + alter_job +---------------------------------------------------------------------------------------------------------------- + (1005,"@ 5 secs","@ 0",-1,"@ 5 mins",t,"{""drop_after"": ""4 days""}",-infinity,public.test_config_check_proc) +(1 row) + +-- test that jobs with an incorrect check function signature will not be registered +-- these are all incorrect function signatures +CREATE OR REPLACE FUNCTION test_config_check_func_0args() RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'I take no arguments and will validate anything you give me!'; +END +$$ LANGUAGE PLPGSQL; +CREATE OR REPLACE FUNCTION test_config_check_func_2args(config jsonb, intarg int) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'I take two arguments (jsonb, int) and I should fail to run!'; +END +$$ LANGUAGE PLPGSQL; +CREATE OR REPLACE FUNCTION test_config_check_func_intarg(config int) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'I take one argument which is an integer and I should fail to run!'; +END +$$ LANGUAGE PLPGSQL; +-- -- this should fail, it has an incorrect check function +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func_0args'::regproc); +ERROR: function or procedure public.test_config_check_func_0args(config jsonb) not found +-- -- so should this +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func_2args'::regproc); +ERROR: function or procedure public.test_config_check_func_2args(config jsonb) not found +-- and this +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func_intarg'::regproc); +ERROR: function or procedure public.test_config_check_func_intarg(config jsonb) not found +-- and this fails as it calls a nonexistent function +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_nonexistent_check_func'::regproc); +ERROR: function "test_nonexistent_check_func" does not exist at character 82 +-- when called with a valid check function and a NULL config no check should occur +CREATE OR REPLACE FUNCTION test_config_check_func(config jsonb) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'This message will get printed for both NULL and not NULL config'; +END +$$ LANGUAGE PLPGSQL; +SET client_min_messages = NOTICE; +-- check done for both NULL and non-NULL config +select add_job('test_proc_with_check', '5 secs', config => NULL, check_config => 'test_config_check_func'::regproc); +NOTICE: This message will get printed for both NULL and not NULL config + add_job +--------- + 1008 +(1 row) + +-- check done +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func'::regproc) as job_id \gset +NOTICE: This message will get printed for both NULL and not NULL config +-- check function not returning void +CREATE OR REPLACE FUNCTION test_config_check_func_returns_int(config jsonb) RETURNS INT +AS $$ +BEGIN + raise notice 'I print a message, and then I return least(1,2)'; + RETURN LEAST(1, 2); +END +$$ LANGUAGE PLPGSQL; +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func_returns_int'::regproc) as job_id_int \gset +NOTICE: I print a message, and then I return least(1,2) +-- drop the registered check function, verify that alter_job will work and print a warning that +-- the check is being skipped due to the check function missing +ALTER FUNCTION test_config_check_func RENAME TO renamed_func; +select alter_job(:job_id, schedule_interval => '1 hour'); +WARNING: function or procedure public.test_config_check_func(config jsonb) not found, skipping config validation for job 1009 + alter_job +------------------------------------------------------------------------------------ + (1009,"@ 1 hour","@ 0",-1,"@ 5 mins",t,{},-infinity,public.test_config_check_func) +(1 row) + +DROP FUNCTION test_config_check_func_returns_int; +select alter_job(:job_id_int, config => '{"field":"value"}'); +WARNING: function or procedure public.test_config_check_func_returns_int(config jsonb) not found, skipping config validation for job 1010 + alter_job +---------------------------------------------------------------------------------------------------------------------- + (1010,"@ 5 secs","@ 0",-1,"@ 5 mins",t,"{""field"": ""value""}",-infinity,public.test_config_check_func_returns_int) +(1 row) + +-- rename the check function and then call alter_job to register the new name +select alter_job(:job_id, check_config => 'renamed_func'::regproc); +NOTICE: This message will get printed for both NULL and not NULL config + alter_job +-------------------------------------------------------------------------- + (1009,"@ 1 hour","@ 0",-1,"@ 5 mins",t,{},-infinity,public.renamed_func) +(1 row) + +-- run alter again, should get a config check +select alter_job(:job_id, config => '{}'); +NOTICE: This message will get printed for both NULL and not NULL config + alter_job +-------------------------------------------------------------------------- + (1009,"@ 1 hour","@ 0",-1,"@ 5 mins",t,{},-infinity,public.renamed_func) +(1 row) + +-- do not drop the current check function but register a new one +CREATE OR REPLACE FUNCTION substitute_check_func(config jsonb) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'This message is a substitute of the previously printed one'; +END +$$ LANGUAGE PLPGSQL; +-- register the new check +select alter_job(:job_id, check_config => 'substitute_check_func'); +NOTICE: This message is a substitute of the previously printed one + alter_job +----------------------------------------------------------------------------------- + (1009,"@ 1 hour","@ 0",-1,"@ 5 mins",t,{},-infinity,public.substitute_check_func) +(1 row) + +select alter_job(:job_id, config => '{}'); +NOTICE: This message is a substitute of the previously printed one + alter_job +----------------------------------------------------------------------------------- + (1009,"@ 1 hour","@ 0",-1,"@ 5 mins",t,{},-infinity,public.substitute_check_func) +(1 row) + +RESET client_min_messages; +-- test an oid that doesn't exist +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 17424217::regproc); +ERROR: function with OID 17424217 does not exist +\c :TEST_DBNAME :ROLE_SUPERUSER +-- test a function with insufficient privileges +create schema test_schema; +create role user_noexec with login; +grant usage on schema test_schema to user_noexec; +CREATE OR REPLACE FUNCTION test_schema.test_config_check_func_privileges(config jsonb) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'This message will only get printed if privileges suffice'; +END +$$ LANGUAGE PLPGSQL; +revoke execute on function test_schema.test_config_check_func_privileges from public; +-- verify the user doesn't have execute permissions on the function +select has_function_privilege('user_noexec', 'test_schema.test_config_check_func_privileges(jsonb)', 'execute'); + has_function_privilege +------------------------ + f +(1 row) + +\c :TEST_DBNAME user_noexec +-- user_noexec should not have exec permissions on this function +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_schema.test_config_check_func_privileges'::regproc); +ERROR: permission denied for function "test_config_check_func_privileges" +\c :TEST_DBNAME :ROLE_SUPERUSER +-- check that alter_job rejects a check function with invalid signature +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'renamed_func') as job_id_alter \gset +NOTICE: This message will get printed for both NULL and not NULL config +select alter_job(:job_id_alter, check_config => 'test_config_check_func_0args'); +ERROR: function or procedure public.test_config_check_func_0args(config jsonb) not found +select alter_job(:job_id_alter); +NOTICE: This message will get printed for both NULL and not NULL config + alter_job +-------------------------------------------------------------------------- + (1011,"@ 5 secs","@ 0",-1,"@ 5 mins",t,{},-infinity,public.renamed_func) +(1 row) + +-- test that we can unregister the check function +select alter_job(:job_id_alter, check_config => 0); + alter_job +------------------------------------------------------- + (1011,"@ 5 secs","@ 0",-1,"@ 5 mins",t,{},-infinity,) +(1 row) + +-- no message printed now +select alter_job(:job_id_alter, config => '{}'); + alter_job +------------------------------------------------------- + (1011,"@ 5 secs","@ 0",-1,"@ 5 mins",t,{},-infinity,) +(1 row) + +-- test what happens if the check function contains a COMMIT +-- procedure with transaction handling +CREATE OR REPLACE PROCEDURE custom_proc2_jsonb(config jsonb) LANGUAGE PLPGSQL AS +$$ +BEGIN +-- RAISE NOTICE 'Starting some transactions inside procedure'; + INSERT INTO custom_log VALUES(1, $1, 'custom_proc 1 COMMIT'); + COMMIT; +END +$$; +select add_job('test_proc_with_check', '5 secs', config => '{}') as job_id_err \gset +select alter_job(:job_id_err, check_config => 'custom_proc2_jsonb'); +ERROR: portal snapshots (0) did not account for all active snapshots (1) +select alter_job(:job_id_err, schedule_interval => '3 minutes'); + alter_job +------------------------------------------------------- + (1012,"@ 3 mins","@ 0",-1,"@ 5 mins",t,{},-infinity,) +(1 row) + +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'custom_proc2_jsonb') as job_id_commit \gset +ERROR: portal snapshots (0) did not account for all active snapshots (1) +-- test the case where we have a background job that registers jobs with a check fn +CREATE OR REPLACE PROCEDURE add_scheduled_jobs_with_check(job_id int, config jsonb) LANGUAGE PLPGSQL AS +$$ +BEGIN + perform add_job('test_proc_with_check', schedule_interval => '10 secs', config => '{}', check_config => 'renamed_func'); +END +$$; +select add_job('add_scheduled_jobs_with_check', schedule_interval => '1 hour') as last_job_id \gset +-- wait for enough time +SELECT wait_for_job_to_run(:last_job_id, 1); + wait_for_job_to_run +--------------------- + t +(1 row) + +select total_runs, total_successes, last_run_status from timescaledb_information.job_stats where job_id = :last_job_id; + total_runs | total_successes | last_run_status +------------+-----------------+----------------- + 1 | 1 | Success +(1 row) + +-- test coverage for alter_job +-- registering an invalid oid +select alter_job(:job_id_alter, check_config => 123456789::regproc); +ERROR: function with OID 123456789 does not exist +-- registering a function with insufficient privileges +\c :TEST_DBNAME user_noexec +select * from add_job('test_proc_with_check', '5 secs', config => '{}') as job_id_owner \gset +select * from alter_job(:job_id_owner, check_config => 'test_schema.test_config_check_func_privileges'::regproc); +ERROR: permission denied for function "test_config_check_func_privileges" +\c :TEST_DBNAME :ROLE_SUPERUSER +DROP SCHEMA test_schema CASCADE; +NOTICE: drop cascades to function test_schema.test_config_check_func_privileges(jsonb) +DROP ROLE user_noexec; +-- test with aggregate check proc +create function jsonb_add (j1 jsonb, j2 jsonb) returns jsonb +AS $$ +BEGIN + RETURN j1 || j2; +END +$$ LANGUAGE PLPGSQL; +create table jsonb_values (j jsonb, i int); +insert into jsonb_values values ('{"refresh_after":"2 weeks"}', 1), ('{"compress_after":"2 weeks"}', 2), ('{"drop_after":"2 weeks"}', 3); +CREATE AGGREGATE sum_jsb (jsonb) +( + sfunc = jsonb_add, + stype = jsonb, + initcond = '{}' +); +-- for test coverage, check unsupported aggregate type +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'sum_jsb'::regproc); +ERROR: unsupported function type diff --git a/tsl/test/expected/bgw_db_scheduler.out b/tsl/test/expected/bgw_db_scheduler.out index f620b1cbc..2ab2e3d35 100644 --- a/tsl/test/expected/bgw_db_scheduler.out +++ b/tsl/test/expected/bgw_db_scheduler.out @@ -1420,9 +1420,9 @@ SELECT wait_for_timer_to_run(0); SELECT insert_job('another', 'bgw_test_job_1', INTERVAL '100ms', INTERVAL '100s', INTERVAL '1s') AS job_id \gset -- call alter_job to trigger cache invalidation SELECT alter_job(:job_id,scheduled:=true); - alter_job ----------------------------------------------------------------- - (1026,"@ 0.1 secs","@ 1 min 40 secs",5,"@ 1 sec",t,,-infinity) + alter_job +----------------------------------------------------------------- + (1026,"@ 0.1 secs","@ 1 min 40 secs",5,"@ 1 sec",t,,-infinity,) (1 row) SELECT ts_bgw_params_reset_time(50000, true); @@ -1522,9 +1522,9 @@ SELECT wait_for_timer_to_run(400000); SELECT insert_job('new_job', 'bgw_test_job_1', INTERVAL '10ms', INTERVAL '100s', INTERVAL '1s') AS job_id \gset -- call alter_job to trigger cache invalidation SELECT alter_job(:job_id,scheduled:=true); - alter_job ------------------------------------------------------------------ - (1027,"@ 0.01 secs","@ 1 min 40 secs",5,"@ 1 sec",t,,-infinity) + alter_job +------------------------------------------------------------------ + (1027,"@ 0.01 secs","@ 1 min 40 secs",5,"@ 1 sec",t,,-infinity,) (1 row) SELECT ts_bgw_params_reset_time(450000, true); diff --git a/tsl/test/expected/bgw_reorder_drop_chunks.out b/tsl/test/expected/bgw_reorder_drop_chunks.out index b3322ff98..ac765f15f 100644 --- a/tsl/test/expected/bgw_reorder_drop_chunks.out +++ b/tsl/test/expected/bgw_reorder_drop_chunks.out @@ -102,9 +102,9 @@ SELECT count(*) FROM _timescaledb_config.bgw_job WHERE proc_schema = '_timescale -- job was created SELECT * FROM timescaledb_information.jobs WHERE job_id=:reorder_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+------------+-------------------+-------------------- - 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | | public | test_reorder_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+------------+-------------------+--------------------+-----------------------+---------------------- + 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | | public | test_reorder_table | _timescaledb_internal | policy_reorder_check (1 row) -- no stats @@ -138,9 +138,9 @@ SELECT * FROM sorted_bgw_log; (2 rows) SELECT * FROM timescaledb_information.jobs WHERE job_id=:reorder_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+------------------------------+-------------------+-------------------- - 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | Fri Dec 31 16:00:00 1999 PST | public | test_reorder_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+------------------------------+-------------------+--------------------+-----------------------+---------------------- + 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | Fri Dec 31 16:00:00 1999 PST | public | test_reorder_table | _timescaledb_internal | policy_reorder_check (1 row) -- job ran once, successfully @@ -178,9 +178,9 @@ SELECT * FROM sorted_bgw_log; (4 rows) SELECT * FROM timescaledb_information.jobs WHERE job_id=:reorder_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+----------------------------------+-------------------+-------------------- - 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | Fri Dec 31 16:00:00.025 1999 PST | public | test_reorder_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+----------------------------------+-------------------+--------------------+-----------------------+---------------------- + 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | Fri Dec 31 16:00:00.025 1999 PST | public | test_reorder_table | _timescaledb_internal | policy_reorder_check (1 row) -- two runs @@ -222,9 +222,9 @@ SELECT * FROM sorted_bgw_log; (6 rows) SELECT * FROM timescaledb_information.jobs WHERE job_id=:reorder_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+---------------------------------+-------------------+-------------------- - 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | Tue Jan 04 16:00:00.05 2000 PST | public | test_reorder_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+---------------------------------+-------------------+--------------------+-----------------------+---------------------- + 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | Tue Jan 04 16:00:00.05 2000 PST | public | test_reorder_table | _timescaledb_internal | policy_reorder_check (1 row) SELECT * @@ -266,9 +266,9 @@ SELECT * FROM sorted_bgw_log; (7 rows) SELECT * FROM timescaledb_information.jobs WHERE job_id=:reorder_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+---------------------------------+-------------------+-------------------- - 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | Tue Jan 04 16:00:00.05 2000 PST | public | test_reorder_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-----------------------+-------------------+-------------+-------------+--------------+-----------------------+----------------+-------------------+-----------+-------------------------------------------------------------------+---------------------------------+-------------------+--------------------+-----------------------+---------------------- + 1000 | Reorder Policy [1000] | @ 4 days | @ 0 | -1 | @ 5 mins | _timescaledb_internal | policy_reorder | default_perm_user | t | {"index_name": "test_reorder_table_time_idx", "hypertable_id": 1} | Tue Jan 04 16:00:00.05 2000 PST | public | test_reorder_table | _timescaledb_internal | policy_reorder_check (1 row) SELECT * @@ -305,8 +305,8 @@ SELECT remove_reorder_policy('test_reorder_table'); (1 row) SELECT * FROM timescaledb_information.jobs WHERE job_id=:reorder_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+------------------+-------------------+-------------+-------------+--------------+-------------+-----------+-------+-----------+--------+------------+-------------------+----------------- + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+------------------+-------------------+-------------+-------------+--------------+-------------+-----------+-------+-----------+--------+------------+-------------------+-----------------+--------------+------------ (0 rows) SELECT job_id, next_start, last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes @@ -336,8 +336,8 @@ SELECT * FROM sorted_bgw_log; (8 rows) SELECT * FROM timescaledb_information.jobs WHERE job_id=:reorder_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+------------------+-------------------+-------------+-------------+--------------+-------------+-----------+-------+-----------+--------+------------+-------------------+----------------- + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+------------------+-------------------+-------------+-------------+--------------+-------------+-----------+-------+-----------+--------+------------+-------------------+-----------------+--------------+------------ (0 rows) -- still only 3 chunks clustered @@ -409,15 +409,15 @@ SELECT count(*) FROM _timescaledb_config.bgw_job WHERE proc_schema = '_timescale (1 row) SELECT alter_job(:drop_chunks_job_id, schedule_interval => INTERVAL '1 second'); - alter_job --------------------------------------------------------------------------------------------------------------- - (1001,"@ 1 sec","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": ""@ 4 mons"", ""hypertable_id"": 2}",-infinity) + alter_job +----------------------------------------------------------------------------------------------------------------------------------------------------------- + (1001,"@ 1 sec","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": ""@ 4 mons"", ""hypertable_id"": 2}",-infinity,_timescaledb_internal.policy_retention_check) (1 row) SELECT * FROM timescaledb_information.jobs WHERE job_id=:drop_chunks_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-------------------------+-------------------+-------------+-------------+--------------+-----------------------+------------------+-------------------+-----------+------------------------------------------------+------------+-------------------+------------------------ - 1001 | Retention Policy [1001] | @ 1 sec | @ 5 mins | -1 | @ 5 mins | _timescaledb_internal | policy_retention | default_perm_user | t | {"drop_after": "@ 4 mons", "hypertable_id": 2} | | public | test_drop_chunks_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-------------------------+-------------------+-------------+-------------+--------------+-----------------------+------------------+-------------------+-----------+------------------------------------------------+------------+-------------------+------------------------+-----------------------+------------------------ + 1001 | Retention Policy [1001] | @ 1 sec | @ 5 mins | -1 | @ 5 mins | _timescaledb_internal | policy_retention | default_perm_user | t | {"drop_after": "@ 4 mons", "hypertable_id": 2} | | public | test_drop_chunks_table | _timescaledb_internal | policy_retention_check (1 row) -- no stats @@ -454,9 +454,9 @@ SELECT * FROM sorted_bgw_log; (2 rows) SELECT * FROM timescaledb_information.jobs WHERE job_id=:drop_chunks_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-------------------------+-------------------+-------------+-------------+--------------+-----------------------+------------------+-------------------+-----------+------------------------------------------------+------------------------------+-------------------+------------------------ - 1001 | Retention Policy [1001] | @ 1 sec | @ 5 mins | -1 | @ 5 mins | _timescaledb_internal | policy_retention | default_perm_user | t | {"drop_after": "@ 4 mons", "hypertable_id": 2} | Fri Dec 31 16:00:01 1999 PST | public | test_drop_chunks_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-------------------------+-------------------+-------------+-------------+--------------+-----------------------+------------------+-------------------+-----------+------------------------------------------------+------------------------------+-------------------+------------------------+-----------------------+------------------------ + 1001 | Retention Policy [1001] | @ 1 sec | @ 5 mins | -1 | @ 5 mins | _timescaledb_internal | policy_retention | default_perm_user | t | {"drop_after": "@ 4 mons", "hypertable_id": 2} | Fri Dec 31 16:00:01 1999 PST | public | test_drop_chunks_table | _timescaledb_internal | policy_retention_check (1 row) -- job ran once, successfully @@ -493,9 +493,9 @@ SELECT * FROM sorted_bgw_log; (3 rows) SELECT * FROM timescaledb_information.jobs WHERE job_id=:drop_chunks_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-------------------------+-------------------+-------------+-------------+--------------+-----------------------+------------------+-------------------+-----------+------------------------------------------------+------------------------------+-------------------+------------------------ - 1001 | Retention Policy [1001] | @ 1 sec | @ 5 mins | -1 | @ 5 mins | _timescaledb_internal | policy_retention | default_perm_user | t | {"drop_after": "@ 4 mons", "hypertable_id": 2} | Fri Dec 31 16:00:01 1999 PST | public | test_drop_chunks_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-------------------------+-------------------+-------------+-------------+--------------+-----------------------+------------------+-------------------+-----------+------------------------------------------------+------------------------------+-------------------+------------------------+-----------------------+------------------------ + 1001 | Retention Policy [1001] | @ 1 sec | @ 5 mins | -1 | @ 5 mins | _timescaledb_internal | policy_retention | default_perm_user | t | {"drop_after": "@ 4 mons", "hypertable_id": 2} | Fri Dec 31 16:00:01 1999 PST | public | test_drop_chunks_table | _timescaledb_internal | policy_retention_check (1 row) -- still only 1 run @@ -545,9 +545,9 @@ SELECT * FROM sorted_bgw_log; (6 rows) SELECT * FROM timescaledb_information.jobs WHERE job_id=:drop_chunks_job_id; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+-------------------------+-------------------+-------------+-------------+--------------+-----------------------+------------------+-------------------+-----------+------------------------------------------------+------------------------------+-------------------+------------------------ - 1001 | Retention Policy [1001] | @ 1 sec | @ 5 mins | -1 | @ 5 mins | _timescaledb_internal | policy_retention | default_perm_user | t | {"drop_after": "@ 4 mons", "hypertable_id": 2} | Fri Dec 31 16:00:02 1999 PST | public | test_drop_chunks_table + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+-------------------------+-------------------+-------------+-------------+--------------+-----------------------+------------------+-------------------+-----------+------------------------------------------------+------------------------------+-------------------+------------------------+-----------------------+------------------------ + 1001 | Retention Policy [1001] | @ 1 sec | @ 5 mins | -1 | @ 5 mins | _timescaledb_internal | policy_retention | default_perm_user | t | {"drop_after": "@ 4 mons", "hypertable_id": 2} | Fri Dec 31 16:00:02 1999 PST | public | test_drop_chunks_table | _timescaledb_internal | policy_retention_check (1 row) -- 2 runs @@ -622,16 +622,16 @@ SELECT add_retention_policy('test_drop_chunks_table_tsntz', INTERVAL '4 months') -- Test that retention policy is being logged SELECT alter_job(id,config:=jsonb_set(config,'{verbose_log}', 'true')) FROM _timescaledb_config.bgw_job WHERE id = :drop_chunks_date_job_id; - alter_job -------------------------------------------------------------------------------------------------------------------------------------- - (1002,"@ 1 day","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": ""@ 4 mons"", ""verbose_log"": true, ""hypertable_id"": 3}",-infinity) + alter_job +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1002,"@ 1 day","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": ""@ 4 mons"", ""verbose_log"": true, ""hypertable_id"": 3}",-infinity,_timescaledb_internal.policy_retention_check) (1 row) SELECT alter_job(id,config:=jsonb_set(config,'{verbose_log}', 'true')) FROM _timescaledb_config.bgw_job WHERE id = :drop_chunks_tsntz_job_id; - alter_job -------------------------------------------------------------------------------------------------------------------------------------- - (1003,"@ 1 day","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": ""@ 4 mons"", ""verbose_log"": true, ""hypertable_id"": 4}",-infinity) + alter_job +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1003,"@ 1 day","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": ""@ 4 mons"", ""verbose_log"": true, ""hypertable_id"": 4}",-infinity,_timescaledb_internal.policy_retention_check) (1 row) CALL run_job(:drop_chunks_date_job_id); diff --git a/tsl/test/expected/cagg_bgw.out b/tsl/test/expected/cagg_bgw.out index f7aaf2955..d3cc12ba0 100644 --- a/tsl/test/expected/cagg_bgw.out +++ b/tsl/test/expected/cagg_bgw.out @@ -269,9 +269,9 @@ SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint --alter the refresh interval and check if next_start is altered SELECT alter_job(:job_id, schedule_interval => '1m', retry_period => '1m'); - alter_job ----------------------------------------------------------------------------------------------------------------------------------------------- - (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:01:00 2000 PST") + alter_job +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:01:00 2000 PST",_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT job_id, next_start - last_finish as until_next, total_runs @@ -309,9 +309,9 @@ SELECT (next_start - '30s'::interval) AS "NEW_NEXT_START" FROM _timescaledb_internal.bgw_job_stat WHERE job_id=:job_id \gset SELECT alter_job(:job_id, next_start => :'NEW_NEXT_START'); - alter_job ----------------------------------------------------------------------------------------------------------------------------------------------- - (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:02:30 2000 PST") + alter_job +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:02:30 2000 PST",_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint * 1000000)+(extract(epoch from interval '2 minute 30 seconds')::bigint * 1000000), true); diff --git a/tsl/test/expected/cagg_bgw_dist_ht.out b/tsl/test/expected/cagg_bgw_dist_ht.out index 1cf4042d3..6baa5a317 100644 --- a/tsl/test/expected/cagg_bgw_dist_ht.out +++ b/tsl/test/expected/cagg_bgw_dist_ht.out @@ -301,9 +301,9 @@ SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint --alter the refresh interval and check if next_start is altered SELECT alter_job(:job_id, schedule_interval => '1m', retry_period => '1m'); - alter_job ----------------------------------------------------------------------------------------------------------------------------------------------- - (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:01:00 2000 PST") + alter_job +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:01:00 2000 PST",_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT job_id, next_start - last_finish as until_next, total_runs @@ -341,9 +341,9 @@ SELECT (next_start - '30s'::interval) AS "NEW_NEXT_START" FROM _timescaledb_internal.bgw_job_stat WHERE job_id=:job_id \gset SELECT alter_job(:job_id, next_start => :'NEW_NEXT_START'); - alter_job ----------------------------------------------------------------------------------------------------------------------------------------------- - (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:02:30 2000 PST") + alter_job +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 min","@ 0",-1,"@ 1 min",t,"{""end_offset"": 4, ""start_offset"": null, ""mat_hypertable_id"": 2}","Sat Jan 01 04:02:30 2000 PST",_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT ts_bgw_params_reset_time((extract(epoch from interval '12 hour')::bigint * 1000000)+(extract(epoch from interval '2 minute 30 seconds')::bigint * 1000000), true); diff --git a/tsl/test/expected/cagg_bgw_drop_chunks.out b/tsl/test/expected/cagg_bgw_drop_chunks.out index f17b26a9b..d0d349ed9 100644 --- a/tsl/test/expected/cagg_bgw_drop_chunks.out +++ b/tsl/test/expected/cagg_bgw_drop_chunks.out @@ -97,9 +97,9 @@ WHERE hypertable_name = '_materialized_hypertable_2' ORDER BY range_start_intege SELECT add_retention_policy( 'drop_chunks_view1', drop_after => 10) as drop_chunks_job_id1 \gset SELECT alter_job(:drop_chunks_job_id1, schedule_interval => INTERVAL '1 second'); - alter_job ----------------------------------------------------------------------------------------------------- - (1000,"@ 1 sec","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": 10, ""hypertable_id"": 2}",-infinity) + alter_job +------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 sec","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": 10, ""hypertable_id"": 2}",-infinity,_timescaledb_internal.policy_retention_check) (1 row) SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(2000000); diff --git a/tsl/test/expected/cagg_policy.out b/tsl/test/expected/cagg_policy.out index 0b7e020e2..569ccbe78 100644 --- a/tsl/test/expected/cagg_policy.out +++ b/tsl/test/expected/cagg_policy.out @@ -579,9 +579,9 @@ SELECT _timescaledb_internal.alter_job_set_hypertable_id( :job_id, 'max_mat_view (1 row) SELECT * FROM timescaledb_information.jobs WHERE job_id != 1 ORDER BY 1; - job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name ---------+----------------------------+-------------------+-------------+-------------+--------------+-------------+-------------+-------------------+-----------+----------------------+------------+-----------------------+---------------------------- - 1026 | User-Defined Action [1026] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_func | default_perm_user | t | {"type": "function"} | | _timescaledb_internal | _materialized_hypertable_5 + job_id | application_name | schedule_interval | max_runtime | max_retries | retry_period | proc_schema | proc_name | owner | scheduled | config | next_start | hypertable_schema | hypertable_name | check_schema | check_name +--------+----------------------------+-------------------+-------------+-------------+--------------+-------------+-------------+-------------------+-----------+----------------------+------------+-----------------------+----------------------------+--------------+------------ + 1026 | User-Defined Action [1026] | @ 1 hour | @ 0 | -1 | @ 5 mins | public | custom_func | default_perm_user | t | {"type": "function"} | | _timescaledb_internal | _materialized_hypertable_5 | | (1 row) SELECT timescaledb_experimental.remove_all_policies('max_mat_view_date', true); -- ignore custom job diff --git a/tsl/test/expected/cagg_usage.out b/tsl/test/expected/cagg_usage.out index 7b4792cdc..229a934c5 100644 --- a/tsl/test/expected/cagg_usage.out +++ b/tsl/test/expected/cagg_usage.out @@ -101,9 +101,9 @@ SELECT schedule_interval FROM _timescaledb_config.bgw_job WHERE id = 1000; -- You can change this setting with ALTER VIEW (equivalently, specify in WITH clause of CREATE VIEW) SELECT alter_job(1000, schedule_interval := '1h'); - alter_job ----------------------------------------------------------------------------------------------------------------------------------------- - (1000,"@ 1 hour","@ 0",-1,"@ 2 hours",t,"{""end_offset"": ""@ 2 hours"", ""start_offset"": null, ""mat_hypertable_id"": 2}",-infinity) + alter_job +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 hour","@ 0",-1,"@ 2 hours",t,"{""end_offset"": ""@ 2 hours"", ""start_offset"": null, ""mat_hypertable_id"": 2}",-infinity,_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT schedule_interval FROM _timescaledb_config.bgw_job WHERE id = 1000; diff --git a/tsl/test/expected/compress_bgw_reorder_drop_chunks.out b/tsl/test/expected/compress_bgw_reorder_drop_chunks.out index 3ee50ff4d..30a854b49 100644 --- a/tsl/test/expected/compress_bgw_reorder_drop_chunks.out +++ b/tsl/test/expected/compress_bgw_reorder_drop_chunks.out @@ -81,9 +81,9 @@ SELECT count(*) FROM _timescaledb_config.bgw_job WHERE proc_schema = '_timescale (1 row) SELECT alter_job(:retention_job_id, schedule_interval => INTERVAL '1 second'); - alter_job --------------------------------------------------------------------------------------------------------------- - (1000,"@ 1 sec","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": ""@ 4 mons"", ""hypertable_id"": 1}",-infinity) + alter_job +----------------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 sec","@ 5 mins",-1,"@ 5 mins",t,"{""drop_after"": ""@ 4 mons"", ""hypertable_id"": 1}",-infinity,_timescaledb_internal.policy_retention_check) (1 row) SELECT * FROM _timescaledb_config.bgw_job where id=:retention_job_id; diff --git a/tsl/test/expected/compression_bgw.out b/tsl/test/expected/compression_bgw.out index bce201067..4e44480d1 100644 --- a/tsl/test/expected/compression_bgw.out +++ b/tsl/test/expected/compression_bgw.out @@ -38,17 +38,17 @@ select * from _timescaledb_config.bgw_job where id = :compressjob_id; (1 row) select * from alter_job(:compressjob_id, schedule_interval=>'1s'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+-------------+-------------+--------------+-----------+-----------------------------------------------------+------------ - 1000 | @ 1 sec | @ 0 | -1 | @ 1 hour | t | {"hypertable_id": 1, "compress_after": "@ 60 days"} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+-------------+-------------+--------------+-----------+-----------------------------------------------------+------------+------------------------------------------------ + 1000 | @ 1 sec | @ 0 | -1 | @ 1 hour | t | {"hypertable_id": 1, "compress_after": "@ 60 days"} | -infinity | _timescaledb_internal.policy_compression_check (1 row) --enable maxchunks to 1 so that only 1 chunk is compressed by the job SELECT alter_job(id,config:=jsonb_set(config,'{maxchunks_to_compress}', '1')) FROM _timescaledb_config.bgw_job WHERE id = :compressjob_id; - alter_job --------------------------------------------------------------------------------------------------------------------------------------------- - (1000,"@ 1 sec","@ 0",-1,"@ 1 hour",t,"{""hypertable_id"": 1, ""compress_after"": ""@ 60 days"", ""maxchunks_to_compress"": 1}",-infinity) + alter_job +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 sec","@ 0",-1,"@ 1 hour",t,"{""hypertable_id"": 1, ""compress_after"": ""@ 60 days"", ""maxchunks_to_compress"": 1}",-infinity,_timescaledb_internal.policy_compression_check) (1 row) select * from _timescaledb_config.bgw_job where id >= 1000 ORDER BY id; @@ -328,16 +328,16 @@ SELECT add_compression_policy AS job_id -- job compresses only 1 chunk at a time -- SELECT alter_job(id,config:=jsonb_set(config,'{maxchunks_to_compress}', '1')) FROM _timescaledb_config.bgw_job WHERE id = :job_id; - alter_job ----------------------------------------------------------------------------------------------------------------------------------------------- - (1004,"@ 12 hours","@ 0",-1,"@ 1 hour",t,"{""hypertable_id"": 11, ""compress_after"": ""@ 1 day"", ""maxchunks_to_compress"": 1}",-infinity) + alter_job +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1004,"@ 12 hours","@ 0",-1,"@ 1 hour",t,"{""hypertable_id"": 11, ""compress_after"": ""@ 1 day"", ""maxchunks_to_compress"": 1}",-infinity,_timescaledb_internal.policy_compression_check) (1 row) SELECT alter_job(id,config:=jsonb_set(config,'{verbose_log}', 'true')) FROM _timescaledb_config.bgw_job WHERE id = :job_id; - alter_job ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- - (1004,"@ 12 hours","@ 0",-1,"@ 1 hour",t,"{""verbose_log"": true, ""hypertable_id"": 11, ""compress_after"": ""@ 1 day"", ""maxchunks_to_compress"": 1}",-infinity) + alter_job +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1004,"@ 12 hours","@ 0",-1,"@ 1 hour",t,"{""verbose_log"": true, ""hypertable_id"": 11, ""compress_after"": ""@ 1 day"", ""maxchunks_to_compress"": 1}",-infinity,_timescaledb_internal.policy_compression_check) (1 row) set client_min_messages TO LOG; @@ -553,9 +553,9 @@ SELECT chunk_status FROM compressed_chunk_info_view WHERE hypertable_name = 'met -- disable recompress in compress job SELECT alter_job(id,config:=jsonb_set(config,'{recompress}','false')) FROM _timescaledb_config.bgw_job WHERE id = :JOB_COMPRESS; - alter_job --------------------------------------------------------------------------------------------------------------------------------------- - (1006,"@ 7 days","@ 0",-1,"@ 5 mins",t,"{""recompress"": false, ""hypertable_id"": 16, ""compress_after"": ""@ 7 days""}",-infinity) + alter_job +--------------------------------------------------------------------------------------------------------------------------------------- + (1006,"@ 7 days","@ 0",-1,"@ 5 mins",t,"{""recompress"": false, ""hypertable_id"": 16, ""compress_after"": ""@ 7 days""}",-infinity,) (1 row) -- nothing to do @@ -587,9 +587,9 @@ SELECT chunk_status FROM compressed_chunk_info_view WHERE hypertable_name = 'met -- reenable recompress in compress job SELECT alter_job(id,config:=jsonb_set(config,'{recompress}','true')) FROM _timescaledb_config.bgw_job WHERE id = :JOB_COMPRESS; - alter_job -------------------------------------------------------------------------------------------------------------------------------------- - (1006,"@ 7 days","@ 0",-1,"@ 5 mins",t,"{""recompress"": true, ""hypertable_id"": 16, ""compress_after"": ""@ 7 days""}",-infinity) + alter_job +-------------------------------------------------------------------------------------------------------------------------------------- + (1006,"@ 7 days","@ 0",-1,"@ 5 mins",t,"{""recompress"": true, ""hypertable_id"": 16, ""compress_after"": ""@ 7 days""}",-infinity,) (1 row) -- should recompress now diff --git a/tsl/test/expected/continuous_aggs.out b/tsl/test/expected/continuous_aggs.out index d9d642574..84ecf3619 100644 --- a/tsl/test/expected/continuous_aggs.out +++ b/tsl/test/expected/continuous_aggs.out @@ -855,9 +855,9 @@ SELECT add_continuous_aggregate_policy('mat_with_test', NULL, '5 h'::interval, ' (1 row) SELECT alter_job(id, schedule_interval => '1h') FROM _timescaledb_config.bgw_job; - alter_job ------------------------------------------------------------------------------------------------------------------------------------------- - (1001,"@ 1 hour","@ 0",-1,"@ 12 hours",t,"{""end_offset"": ""@ 5 hours"", ""start_offset"": null, ""mat_hypertable_id"": 20}",-infinity) + alter_job +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1001,"@ 1 hour","@ 0",-1,"@ 12 hours",t,"{""end_offset"": ""@ 5 hours"", ""start_offset"": null, ""mat_hypertable_id"": 20}",-infinity,_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT schedule_interval FROM _timescaledb_config.bgw_job; @@ -867,9 +867,9 @@ SELECT schedule_interval FROM _timescaledb_config.bgw_job; (1 row) SELECT alter_job(id, schedule_interval => '2h') FROM _timescaledb_config.bgw_job; - alter_job -------------------------------------------------------------------------------------------------------------------------------------------- - (1001,"@ 2 hours","@ 0",-1,"@ 12 hours",t,"{""end_offset"": ""@ 5 hours"", ""start_offset"": null, ""mat_hypertable_id"": 20}",-infinity) + alter_job +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1001,"@ 2 hours","@ 0",-1,"@ 12 hours",t,"{""end_offset"": ""@ 5 hours"", ""start_offset"": null, ""mat_hypertable_id"": 20}",-infinity,_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT schedule_interval FROM _timescaledb_config.bgw_job; @@ -948,9 +948,9 @@ SELECT add_continuous_aggregate_policy('mat_with_test', NULL, 500::integer, '12 (1 row) SELECT alter_job(id, schedule_interval => '2h') FROM _timescaledb_config.bgw_job; - alter_job ---------------------------------------------------------------------------------------------------------------------------------- - (1002,"@ 2 hours","@ 0",-1,"@ 12 hours",t,"{""end_offset"": 500, ""start_offset"": null, ""mat_hypertable_id"": 23}",-infinity) + alter_job +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1002,"@ 2 hours","@ 0",-1,"@ 12 hours",t,"{""end_offset"": 500, ""start_offset"": null, ""mat_hypertable_id"": 23}",-infinity,_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT schedule_interval FROM _timescaledb_config.bgw_job; diff --git a/tsl/test/expected/continuous_aggs_deprecated.out b/tsl/test/expected/continuous_aggs_deprecated.out index eeb60fdb1..c7491f786 100644 --- a/tsl/test/expected/continuous_aggs_deprecated.out +++ b/tsl/test/expected/continuous_aggs_deprecated.out @@ -869,9 +869,9 @@ SELECT add_continuous_aggregate_policy('mat_with_test', NULL, '5 h'::interval, ' (1 row) SELECT alter_job(id, schedule_interval => '1h') FROM _timescaledb_config.bgw_job; - alter_job ------------------------------------------------------------------------------------------------------------------------------------------- - (1001,"@ 1 hour","@ 0",-1,"@ 12 hours",t,"{""end_offset"": ""@ 5 hours"", ""start_offset"": null, ""mat_hypertable_id"": 20}",-infinity) + alter_job +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1001,"@ 1 hour","@ 0",-1,"@ 12 hours",t,"{""end_offset"": ""@ 5 hours"", ""start_offset"": null, ""mat_hypertable_id"": 20}",-infinity,_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT schedule_interval FROM _timescaledb_config.bgw_job; @@ -881,9 +881,9 @@ SELECT schedule_interval FROM _timescaledb_config.bgw_job; (1 row) SELECT alter_job(id, schedule_interval => '2h') FROM _timescaledb_config.bgw_job; - alter_job -------------------------------------------------------------------------------------------------------------------------------------------- - (1001,"@ 2 hours","@ 0",-1,"@ 12 hours",t,"{""end_offset"": ""@ 5 hours"", ""start_offset"": null, ""mat_hypertable_id"": 20}",-infinity) + alter_job +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1001,"@ 2 hours","@ 0",-1,"@ 12 hours",t,"{""end_offset"": ""@ 5 hours"", ""start_offset"": null, ""mat_hypertable_id"": 20}",-infinity,_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT schedule_interval FROM _timescaledb_config.bgw_job; @@ -967,9 +967,9 @@ SELECT add_continuous_aggregate_policy('mat_with_test', NULL, 500::integer, '12 (1 row) SELECT alter_job(id, schedule_interval => '2h') FROM _timescaledb_config.bgw_job; - alter_job ---------------------------------------------------------------------------------------------------------------------------------- - (1002,"@ 2 hours","@ 0",-1,"@ 12 hours",t,"{""end_offset"": 500, ""start_offset"": null, ""mat_hypertable_id"": 23}",-infinity) + alter_job +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1002,"@ 2 hours","@ 0",-1,"@ 12 hours",t,"{""end_offset"": 500, ""start_offset"": null, ""mat_hypertable_id"": 23}",-infinity,_timescaledb_internal.policy_refresh_continuous_aggregate_check) (1 row) SELECT schedule_interval FROM _timescaledb_config.bgw_job; diff --git a/tsl/test/expected/dist_compression.out b/tsl/test/expected/dist_compression.out index 61452839a..9ca5fde96 100644 --- a/tsl/test/expected/dist_compression.out +++ b/tsl/test/expected/dist_compression.out @@ -660,9 +660,9 @@ select * from _timescaledb_config.bgw_job where id = :compressjob_id; (1 row) select * from alter_job(:compressjob_id, schedule_interval=>'1s'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+-------------+-------------+--------------+-----------+-----------------------------------------------------+------------ - 1000 | @ 1 sec | @ 0 | -1 | @ 1 hour | t | {"hypertable_id": 2, "compress_after": "@ 60 days"} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+-------------+-------------+--------------+-----------+-----------------------------------------------------+------------+------------------------------------------------ + 1000 | @ 1 sec | @ 0 | -1 | @ 1 hour | t | {"hypertable_id": 2, "compress_after": "@ 60 days"} | -infinity | _timescaledb_internal.policy_compression_check (1 row) select * from _timescaledb_config.bgw_job where id >= 1000 ORDER BY id; @@ -674,9 +674,9 @@ select * from _timescaledb_config.bgw_job where id >= 1000 ORDER BY id; -- we want only 1 chunk to be compressed -- SELECT alter_job(id,config:=jsonb_set(config,'{maxchunks_to_compress}', '1')) FROM _timescaledb_config.bgw_job WHERE id = :compressjob_id; - alter_job --------------------------------------------------------------------------------------------------------------------------------------------- - (1000,"@ 1 sec","@ 0",-1,"@ 1 hour",t,"{""hypertable_id"": 2, ""compress_after"": ""@ 60 days"", ""maxchunks_to_compress"": 1}",-infinity) + alter_job +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + (1000,"@ 1 sec","@ 0",-1,"@ 1 hour",t,"{""hypertable_id"": 2, ""compress_after"": ""@ 60 days"", ""maxchunks_to_compress"": 1}",-infinity,_timescaledb_internal.policy_compression_check) (1 row) insert into conditions diff --git a/tsl/test/expected/tsl_tables.out b/tsl/test/expected/tsl_tables.out index 95a18b285..a11b04570 100644 --- a/tsl/test/expected/tsl_tables.out +++ b/tsl/test/expected/tsl_tables.out @@ -626,72 +626,72 @@ select add_reorder_policy('test_table', 'test_table_time_idx') as job_id \gset -- No change select * from alter_job(:job_id); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+-------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 84 hours | @ 0 | -1 | @ 5 mins | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+-------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 84 hours | @ 0 | -1 | @ 5 mins | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) -- Changes expected select * from alter_job(:job_id, INTERVAL '3 years', INTERVAL '5 min', 5, INTERVAL '123 sec'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+-------------+-------------+-----------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 3 years | @ 5 mins | 5 | @ 2 mins 3 secs | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+-------------+-------------+-----------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 3 years | @ 5 mins | 5 | @ 2 mins 3 secs | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) select * from alter_job(:job_id, INTERVAL '123 years'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+-------------+-------------+-----------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 123 years | @ 5 mins | 5 | @ 2 mins 3 secs | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+-------------+-------------+-----------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 123 years | @ 5 mins | 5 | @ 2 mins 3 secs | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) select * from alter_job(:job_id, retry_period => INTERVAL '33 hours'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+-------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 123 years | @ 5 mins | 5 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+-------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 123 years | @ 5 mins | 5 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) select * from alter_job(:job_id, max_runtime => INTERVAL '456 sec'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 123 years | @ 7 mins 36 secs | 5 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 123 years | @ 7 mins 36 secs | 5 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) select * from alter_job(:job_id, max_retries => 0); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 123 years | @ 7 mins 36 secs | 0 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 123 years | @ 7 mins 36 secs | 0 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) select * from alter_job(:job_id, max_retries => -1); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 123 years | @ 7 mins 36 secs | -1 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 123 years | @ 7 mins 36 secs | -1 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) select * from alter_job(:job_id, max_retries => 20); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) -- No change select * from alter_job(:job_id, max_runtime => NULL); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) select * from alter_job(:job_id, max_retries => NULL); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 123 years | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) --change schedule_interval when bgw_job_stat does not exist select * from alter_job(:job_id, schedule_interval=>'1 min'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 1 min | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 1 min | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) select count(*) = 0 from _timescaledb_internal.bgw_job_stat where job_id = :job_id; @@ -702,23 +702,23 @@ select count(*) = 0 from _timescaledb_internal.bgw_job_stat where job_id = :job_ --set next_start when bgw_job_stat does not exist select * from alter_job(:job_id, next_start=>'2001-01-01 01:01:01'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------ - 1014 | @ 1 min | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Mon Jan 01 01:01:01 2001 PST + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------+-------------------------------------------- + 1014 | @ 1 min | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Mon Jan 01 01:01:01 2001 PST | _timescaledb_internal.policy_reorder_check (1 row) --change schedule_interval when no last_finish set select * from alter_job(:job_id, schedule_interval=>'10 min'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 10 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 10 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | -infinity | _timescaledb_internal.policy_reorder_check (1 row) --next_start overrides any schedule_interval changes select * from alter_job(:job_id, schedule_interval=>'20 min', next_start=>'2002-01-01 01:01:01'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------ - 1014 | @ 20 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Tue Jan 01 01:01:01 2002 PST + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------+-------------------------------------------- + 1014 | @ 20 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Tue Jan 01 01:01:01 2002 PST | _timescaledb_internal.policy_reorder_check (1 row) --set the last_finish manually @@ -727,30 +727,30 @@ UPDATE _timescaledb_internal.bgw_job_stat SET last_finish = '2003-01-01:01:01:01 \c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER --not changing the interval doesn't change the next_start select * from alter_job(:job_id, schedule_interval=>'20 min'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------ - 1014 | @ 20 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Tue Jan 01 01:01:01 2002 PST + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------+-------------------------------------------- + 1014 | @ 20 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Tue Jan 01 01:01:01 2002 PST | _timescaledb_internal.policy_reorder_check (1 row) --changing the interval changes next_start select * from alter_job(:job_id, schedule_interval=>'30 min'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------ - 1014 | @ 30 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Wed Jan 01 01:31:01 2003 PST + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------+-------------------------------------------- + 1014 | @ 30 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Wed Jan 01 01:31:01 2003 PST | _timescaledb_internal.policy_reorder_check (1 row) --explicit next start overrides. select * from alter_job(:job_id, schedule_interval=>'40 min', next_start=>'2004-01-01 01:01:01'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------ - 1014 | @ 40 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Thu Jan 01 01:01:01 2004 PST + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------------------------+-------------------------------------------- + 1014 | @ 40 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | Thu Jan 01 01:01:01 2004 PST | _timescaledb_internal.policy_reorder_check (1 row) --test pausing select * from alter_job(:job_id, next_start=>'infinity'); - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------ - 1014 | @ 40 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | infinity + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+------------------+-------------+--------------+-----------+-----------------------------------------------------------+------------+-------------------------------------------- + 1014 | @ 40 mins | @ 7 mins 36 secs | 20 | @ 33 hours | t | {"index_name": "test_table_time_idx", "hypertable_id": 7} | infinity | _timescaledb_internal.policy_reorder_check (1 row) --test that you can use now() to unpause @@ -805,9 +805,9 @@ ERROR: configuration hypertable id 47 not found -- Check if_exists boolean works correctly select * from alter_job(1234, if_exists => TRUE); NOTICE: job 1234 not found, skipping - job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start ---------+-------------------+-------------+-------------+--------------+-----------+--------+------------ - | | | | | | | + job_id | schedule_interval | max_runtime | max_retries | retry_period | scheduled | config | next_start | check_config +--------+-------------------+-------------+-------------+--------------+-----------+--------+------------+-------------- + | | | | | | | | (1 row) \set ON_ERROR_STOP 0 diff --git a/tsl/test/shared/expected/extension.out b/tsl/test/shared/expected/extension.out index b9d566712..ecd0832dc 100644 --- a/tsl/test/shared/expected/extension.out +++ b/tsl/test/shared/expected/extension.out @@ -98,15 +98,15 @@ ORDER BY pronamespace::regnamespace::text COLLATE "C", p.oid::regprocedure::text _timescaledb_internal.partialize_agg(anyelement) _timescaledb_internal.ping_data_node(name) _timescaledb_internal.policy_compression(integer,jsonb) - _timescaledb_internal.policy_compression_check(integer,jsonb) + _timescaledb_internal.policy_compression_check(jsonb) _timescaledb_internal.policy_compression_execute(integer,integer,anyelement,integer,boolean,boolean) _timescaledb_internal.policy_recompression(integer,jsonb) _timescaledb_internal.policy_refresh_continuous_aggregate(integer,jsonb) - _timescaledb_internal.policy_refresh_continuous_aggregate_check(integer,jsonb) + _timescaledb_internal.policy_refresh_continuous_aggregate_check(jsonb) _timescaledb_internal.policy_reorder(integer,jsonb) - _timescaledb_internal.policy_reorder_check(integer,jsonb) + _timescaledb_internal.policy_reorder_check(jsonb) _timescaledb_internal.policy_retention(integer,jsonb) - _timescaledb_internal.policy_retention_check(integer,jsonb) + _timescaledb_internal.policy_retention_check(jsonb) _timescaledb_internal.process_ddl_event() _timescaledb_internal.range_value_to_pretty(bigint,regtype) _timescaledb_internal.relation_size(regclass) @@ -140,7 +140,7 @@ ORDER BY pronamespace::regnamespace::text COLLATE "C", p.oid::regprocedure::text add_job(regproc,interval,jsonb,timestamp with time zone,boolean,regproc) add_reorder_policy(regclass,name,boolean) add_retention_policy(regclass,"any",boolean,interval) - alter_job(integer,interval,interval,integer,interval,boolean,jsonb,timestamp with time zone,boolean) + alter_job(integer,interval,interval,integer,interval,boolean,jsonb,timestamp with time zone,boolean,regproc) approximate_row_count(regclass) attach_data_node(name,regclass,boolean,boolean) attach_tablespace(name,regclass,boolean) diff --git a/tsl/test/sql/bgw_custom.sql b/tsl/test/sql/bgw_custom.sql index 7f404a88c..5d73bc300 100644 --- a/tsl/test/sql/bgw_custom.sql +++ b/tsl/test/sql/bgw_custom.sql @@ -317,3 +317,248 @@ FROM _timescaledb_config.bgw_job WHERE id = :job_id_5; -- Stop Background Workers SELECT _timescaledb_internal.stop_background_workers(); + +SELECT _timescaledb_internal.restart_background_workers(); + +\set ON_ERROR_STOP 0 +-- add test for custom jobs with custom check functions +-- create the functions/procedures to be used as checking functions +CREATE OR REPLACE PROCEDURE test_config_check_proc(config jsonb) +LANGUAGE PLPGSQL +AS $$ +DECLARE + drop_after interval; +BEGIN + SELECT jsonb_object_field_text (config, 'drop_after')::interval INTO STRICT drop_after; + IF drop_after IS NULL THEN + RAISE EXCEPTION 'Config must be not NULL and have drop_after'; + END IF ; +END +$$; + +CREATE OR REPLACE FUNCTION test_config_check_func(config jsonb) RETURNS VOID +AS $$ +DECLARE + drop_after interval; +BEGIN + IF config IS NULL THEN + RETURN; + END IF; + SELECT jsonb_object_field_text (config, 'drop_after')::interval INTO STRICT drop_after; + IF drop_after IS NULL THEN + RAISE EXCEPTION 'Config can be NULL but must have drop_after if not'; + END IF ; +END +$$ LANGUAGE PLPGSQL; + +-- step 2, create a procedure to run as a custom job +CREATE OR REPLACE PROCEDURE test_proc_with_check(job_id int, config jsonb) +LANGUAGE PLPGSQL +AS $$ +BEGIN + RAISE NOTICE 'Will only print this if config passes checks, my config is %', config; +END +$$; + +-- step 3, add the job with the config check function passed as argument +-- test procedures +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_proc'::regproc); +select add_job('test_proc_with_check', '5 secs', config => NULL, check_config => 'test_config_check_proc'::regproc); +select add_job('test_proc_with_check', '5 secs', config => '{"drop_after": "chicken"}', check_config => 'test_config_check_proc'::regproc); +select add_job('test_proc_with_check', '5 secs', config => '{"drop_after": "2 weeks"}', check_config => 'test_config_check_proc'::regproc) +as job_with_proc_check_id \gset + +-- test functions +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func'::regproc); +select add_job('test_proc_with_check', '5 secs', config => NULL, check_config => 'test_config_check_func'::regproc); +select add_job('test_proc_with_check', '5 secs', config => '{"drop_after": "chicken"}', check_config => 'test_config_check_func'::regproc); +select add_job('test_proc_with_check', '5 secs', config => '{"drop_after": "2 weeks"}', check_config => 'test_config_check_func'::regproc) +as job_with_func_check_id \gset + + +--- test alter_job +select alter_job(:job_with_func_check_id, config => '{"drop_after":"chicken"}'); +select alter_job(:job_with_func_check_id, config => '{"drop_after":"5 years"}'); + +select alter_job(:job_with_proc_check_id, config => '{"drop_after":"4 days"}'); + + +-- test that jobs with an incorrect check function signature will not be registered +-- these are all incorrect function signatures + +CREATE OR REPLACE FUNCTION test_config_check_func_0args() RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'I take no arguments and will validate anything you give me!'; +END +$$ LANGUAGE PLPGSQL; + +CREATE OR REPLACE FUNCTION test_config_check_func_2args(config jsonb, intarg int) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'I take two arguments (jsonb, int) and I should fail to run!'; +END +$$ LANGUAGE PLPGSQL; + +CREATE OR REPLACE FUNCTION test_config_check_func_intarg(config int) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'I take one argument which is an integer and I should fail to run!'; +END +$$ LANGUAGE PLPGSQL; + +-- -- this should fail, it has an incorrect check function +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func_0args'::regproc); +-- -- so should this +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func_2args'::regproc); +-- and this +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func_intarg'::regproc); +-- and this fails as it calls a nonexistent function +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_nonexistent_check_func'::regproc); + +-- when called with a valid check function and a NULL config no check should occur +CREATE OR REPLACE FUNCTION test_config_check_func(config jsonb) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'This message will get printed for both NULL and not NULL config'; +END +$$ LANGUAGE PLPGSQL; + +SET client_min_messages = NOTICE; +-- check done for both NULL and non-NULL config +select add_job('test_proc_with_check', '5 secs', config => NULL, check_config => 'test_config_check_func'::regproc); +-- check done +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func'::regproc) as job_id \gset + +-- check function not returning void +CREATE OR REPLACE FUNCTION test_config_check_func_returns_int(config jsonb) RETURNS INT +AS $$ +BEGIN + raise notice 'I print a message, and then I return least(1,2)'; + RETURN LEAST(1, 2); +END +$$ LANGUAGE PLPGSQL; +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_config_check_func_returns_int'::regproc) as job_id_int \gset + +-- drop the registered check function, verify that alter_job will work and print a warning that +-- the check is being skipped due to the check function missing +ALTER FUNCTION test_config_check_func RENAME TO renamed_func; +select alter_job(:job_id, schedule_interval => '1 hour'); +DROP FUNCTION test_config_check_func_returns_int; +select alter_job(:job_id_int, config => '{"field":"value"}'); + +-- rename the check function and then call alter_job to register the new name +select alter_job(:job_id, check_config => 'renamed_func'::regproc); +-- run alter again, should get a config check +select alter_job(:job_id, config => '{}'); +-- do not drop the current check function but register a new one +CREATE OR REPLACE FUNCTION substitute_check_func(config jsonb) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'This message is a substitute of the previously printed one'; +END +$$ LANGUAGE PLPGSQL; +-- register the new check +select alter_job(:job_id, check_config => 'substitute_check_func'); +select alter_job(:job_id, config => '{}'); + +RESET client_min_messages; + +-- test an oid that doesn't exist +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 17424217::regproc); + +\c :TEST_DBNAME :ROLE_SUPERUSER +-- test a function with insufficient privileges +create schema test_schema; +create role user_noexec with login; +grant usage on schema test_schema to user_noexec; + +CREATE OR REPLACE FUNCTION test_schema.test_config_check_func_privileges(config jsonb) RETURNS VOID +AS $$ +BEGIN + RAISE NOTICE 'This message will only get printed if privileges suffice'; +END +$$ LANGUAGE PLPGSQL; + +revoke execute on function test_schema.test_config_check_func_privileges from public; +-- verify the user doesn't have execute permissions on the function +select has_function_privilege('user_noexec', 'test_schema.test_config_check_func_privileges(jsonb)', 'execute'); + +\c :TEST_DBNAME user_noexec +-- user_noexec should not have exec permissions on this function +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'test_schema.test_config_check_func_privileges'::regproc); + +\c :TEST_DBNAME :ROLE_SUPERUSER + +-- check that alter_job rejects a check function with invalid signature +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'renamed_func') as job_id_alter \gset +select alter_job(:job_id_alter, check_config => 'test_config_check_func_0args'); +select alter_job(:job_id_alter); +-- test that we can unregister the check function +select alter_job(:job_id_alter, check_config => 0); +-- no message printed now +select alter_job(:job_id_alter, config => '{}'); + +-- test what happens if the check function contains a COMMIT +-- procedure with transaction handling +CREATE OR REPLACE PROCEDURE custom_proc2_jsonb(config jsonb) LANGUAGE PLPGSQL AS +$$ +BEGIN +-- RAISE NOTICE 'Starting some transactions inside procedure'; + INSERT INTO custom_log VALUES(1, $1, 'custom_proc 1 COMMIT'); + COMMIT; +END +$$; + +select add_job('test_proc_with_check', '5 secs', config => '{}') as job_id_err \gset +select alter_job(:job_id_err, check_config => 'custom_proc2_jsonb'); +select alter_job(:job_id_err, schedule_interval => '3 minutes'); +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'custom_proc2_jsonb') as job_id_commit \gset + +-- test the case where we have a background job that registers jobs with a check fn +CREATE OR REPLACE PROCEDURE add_scheduled_jobs_with_check(job_id int, config jsonb) LANGUAGE PLPGSQL AS +$$ +BEGIN + perform add_job('test_proc_with_check', schedule_interval => '10 secs', config => '{}', check_config => 'renamed_func'); +END +$$; + +select add_job('add_scheduled_jobs_with_check', schedule_interval => '1 hour') as last_job_id \gset +-- wait for enough time +SELECT wait_for_job_to_run(:last_job_id, 1); +select total_runs, total_successes, last_run_status from timescaledb_information.job_stats where job_id = :last_job_id; + +-- test coverage for alter_job +-- registering an invalid oid +select alter_job(:job_id_alter, check_config => 123456789::regproc); +-- registering a function with insufficient privileges +\c :TEST_DBNAME user_noexec +select * from add_job('test_proc_with_check', '5 secs', config => '{}') as job_id_owner \gset +select * from alter_job(:job_id_owner, check_config => 'test_schema.test_config_check_func_privileges'::regproc); + +\c :TEST_DBNAME :ROLE_SUPERUSER +DROP SCHEMA test_schema CASCADE; +DROP ROLE user_noexec; + +-- test with aggregate check proc +create function jsonb_add (j1 jsonb, j2 jsonb) returns jsonb +AS $$ +BEGIN + RETURN j1 || j2; +END +$$ LANGUAGE PLPGSQL; + +create table jsonb_values (j jsonb, i int); +insert into jsonb_values values ('{"refresh_after":"2 weeks"}', 1), ('{"compress_after":"2 weeks"}', 2), ('{"drop_after":"2 weeks"}', 3); + +CREATE AGGREGATE sum_jsb (jsonb) +( + sfunc = jsonb_add, + stype = jsonb, + initcond = '{}' +); + +-- for test coverage, check unsupported aggregate type +select add_job('test_proc_with_check', '5 secs', config => '{}', check_config => 'sum_jsb'::regproc); + +