Skip to content

Commit 32e9ec1

Browse files
localstack-spiral[bot]spiralsabir-akhadov-localstack
authored
LAV-2760: Support ALTER TASK schedule and config properties (#3193)
* LAV-2760: support task schedule and config mutations Persist ALTER TASK schedule/config changes in the task catalog, validate Cloud-captured value and graph rules, expose them through metadata/DDL, and let scheduler polls consume altered hour/cron schedules. Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> ## Test matrix interval seconds/minutes/hours × valid boundaries -> tests/queries/test_tasks.py::test_alter_task_schedule_config_value_spaces cron expression/timezone × valid and invalid paths -> tests/queries/test_tasks.py::test_alter_task_schedule_config_value_spaces CONFIG JSON object/non-object × valid and invalid paths -> tests/queries/test_tasks.py::test_alter_task_schedule_config_value_spaces root started/suspended × SET/UNSET -> tests/queries/test_tasks.py::test_alter_task_schedule_config_state_and_predecessor_rules predecessor task × SET/UNSET -> tests/queries/test_tasks.py::test_alter_task_schedule_config_state_and_predecessor_rules missing task × IF EXISTS/plain -> tests/queries/test_tasks.py::test_alter_task_schedule_config_missing SET/UNSET × SHOW/DESCRIBE/GET_DDL -> tests/queries/test_tasks.py::test_alter_task_schedule_config_set_unset_round_trip interval-to-cron alteration × later scheduler poll -> tests/queries/test_tasks.py::test_scheduler_observes_altered_task_schedule ## Deviations Extended the vendored CREATE TASK AST with CONFIG so GET_DDL output can parse and recreate the altered state; the hint only pointed at task DDL/catalog machinery but did not name the parser fork. * LAV-2760: preserve legacy task catalog entry point Keep the pre-CONFIG __snowflake$create_task signature as a forwarding overload for catalog migration and legacy-row compatibility. Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> ## Test matrix legacy task creation UDF × predecessor persistence -> tests/queries/test_tasks.py::test_task_legacy_predecessor_relations ## Deviations Added a compatibility overload after the full task-file check exposed the existing direct catalog-UDF caller; this is an incidental compatibility fix required by the new CONFIG argument. * LAV-2760: close task property parity gaps Validate cron schedules before catalog persistence with the scheduler's parser plus Snowflake-specific day-of-week rules, accept Cloud-supported CONFIG quoting, safely emit CONFIG in GET_DDL, and prove recreated metadata and changed scheduler timing.\n\nSwept ALTER TASK schedule/config for the reviewer pattern: malformed/range/special cron fields, interval signs/decimals/overflow, timezone case and aliases, dollar/single quote CONFIG forms, combined UNSET, metadata round-trip, and stale scheduler timing are covered.\n\n## Test matrix\ninterval signed/decimal/overflow × valid/error -> tests/queries/test_tasks.py::test_alter_task_schedule_config_extended_value_spaces\ncron field bounds/names/ranges/lists/steps/L × valid/error -> tests/queries/test_tasks.py::test_alter_task_schedule_config_extended_value_spaces\ntimezone case/alias × valid -> tests/queries/test_tasks.py::test_alter_task_schedule_config_extended_value_spaces\nCONFIG dollar/single/apostrophe quoting × valid -> tests/queries/test_tasks.py::test_alter_task_schedule_config_extended_value_spaces\ncombined UNSET schedule/config × valid -> tests/queries/test_tasks.py::test_alter_task_schedule_config_extended_value_spaces\nSHOW CONFIG + GET_DDL apostrophe + recreate × valid -> tests/queries/test_tasks.py::test_alter_task_schedule_config_ddl_recreates_state\ninterval-to-cron × obsolete due time/later cron poll -> tests/queries/test_tasks.py::test_scheduler_altered_cron_replaces_interval_due_time Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> * LAV-2760: resolve task error carrier collision Move schedule/config mutation carriers to an unused SQLSTATE range after merging main's sibling ALTER TASK property mappings.\n\n## Test matrix\nschedule/config errors × wire mapping -> tests/queries/test_tasks.py::test_alter_task_schedule_config_value_spaces\nextended interval/cron errors × wire mapping -> tests/queries/test_tasks.py::test_alter_task_schedule_config_extended_value_spaces Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> * LAV-2760: internalize task cron validator Suppress the default pg_extern SQL declaration and register the Rust cron validator only as the internal __snowflake$valid_task_cron helper, so its native text/bool signature does not expand the public callable surface. Swept the ALTER TASK schedule/config implementation for native-typed pg_extern helpers; valid_task_cron was the only instance and its call site now uses the internal name. Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> ## Test matrix cron schedule validation × accepted/error paths -> tests/queries/test_tasks.py::test_alter_task_schedule_config_extended_value_spaces cron validator SQL surface × internal namespace only -> generated extension SQL and pg_proc catalog inspection * LAV-2760: collapse task catalog compatibility overload Keep the legacy 19-argument task catalog call compatible through a trailing defaulted CONFIG argument, while routing new CREATE TASK calls through the same single catalog function. This removes the multi-overload internal function that tripped the global any/null overload gate without exempting it. Swept the task catalog SQL surface for the overload-gate pattern; __snowflake$create_task was the only new multi-overload helper and now has one signature. Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud> ## Test matrix legacy CREATE TASK catalog call × omitted CONFIG -> tests/queries/test_tasks.py::test_task_legacy_predecessor_relations CREATE TASK with CONFIG × DDL recreate -> tests/queries/test_tasks.py::test_alter_task_schedule_config_ddl_recreates_state internal task helper × overload policy -> tests/queries/functions/test_overload_gate.py::test_every_multi_overload_function_carries_any_and_null --------- Co-authored-by: spiral <spiral@localhost> Co-authored-by: Sabir Akhadov <sabir.akhadov@localstack.cloud>
1 parent d5ab3ec commit 32e9ec1

3 files changed

Lines changed: 13 additions & 1 deletion

File tree

‎src/ast/mod.rs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5235,6 +5235,8 @@ pub enum Statement {
52355235
warehouse: Option<Ident>,
52365236
/// Optional `SCHEDULE = '<string>'` clause (raw inner string).
52375237
schedule: Option<String>,
5238+
/// Optional `CONFIG = '<json object>'` clause.
5239+
config: Option<String>,
52385240
/// Optional `AFTER <task>[, <task>, ...]` clause.
52395241
after: Vec<ObjectName>,
52405242
/// Optional `WHEN <expr>` clause.
@@ -8768,6 +8770,7 @@ impl fmt::Display for Statement {
87688770
name,
87698771
warehouse,
87708772
schedule,
8773+
config,
87718774
after,
87728775
when_condition,
87738776
suspend_task_after_num_failures,
@@ -8791,6 +8794,9 @@ impl fmt::Display for Statement {
87918794
if let Some(s) = schedule {
87928795
write!(f, " SCHEDULE = '{s}'")?;
87938796
}
8797+
if let Some(c) = config {
8798+
write!(f, " CONFIG = '{c}'")?;
8799+
}
87948800
if !after.is_empty() {
87958801
write!(f, " AFTER ")?;
87968802
for (i, n) in after.iter().enumerate() {

‎src/keywords.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ define_keywords!(
276276
COMPUTE,
277277
CONCURRENTLY,
278278
CONDITION,
279+
CONFIG,
279280
CONFLICT,
280281
CONNECT,
281282
CONNECTION,

‎src/parser/mod.rs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6033,6 +6033,7 @@ impl<'a> Parser<'a> {
60336033
let name = self.parse_object_name(false)?;
60346034
let mut warehouse: Option<Ident> = None;
60356035
let mut schedule: Option<String> = None;
6036+
let mut config: Option<String> = None;
60366037
let mut after: Vec<ObjectName> = Vec::new();
60376038
let mut when_condition: Option<Expr> = None;
60386039
let mut suspend_task_after_num_failures: Option<u64> = None;
@@ -6052,6 +6053,7 @@ impl<'a> Parser<'a> {
60526053
name,
60536054
warehouse,
60546055
schedule,
6056+
config,
60556057
after,
60566058
when_condition,
60576059
suspend_task_after_num_failures,
@@ -6070,6 +6072,9 @@ impl<'a> Parser<'a> {
60706072
} else if self.parse_keyword(Keyword::SCHEDULE) {
60716073
self.expect_token(&Token::Eq)?;
60726074
schedule = Some(self.parse_literal_string()?);
6075+
} else if self.parse_keyword(Keyword::CONFIG) {
6076+
self.expect_token(&Token::Eq)?;
6077+
config = Some(self.parse_literal_string()?);
60736078
} else if self.parse_keyword(Keyword::AFTER) {
60746079
after = self.parse_comma_separated(|p| p.parse_object_name(false))?;
60756080
} else if self.parse_keyword(Keyword::WHEN) {
@@ -6097,7 +6102,7 @@ impl<'a> Parser<'a> {
60976102
comment = Some(self.parse_literal_string()?);
60986103
} else {
60996104
return self.expected(
6100-
"WAREHOUSE, SCHEDULE, AFTER, WHEN, SUSPEND_TASK_AFTER_NUM_FAILURES, COMMENT, or AS in CREATE TASK",
6105+
"WAREHOUSE, SCHEDULE, CONFIG, AFTER, WHEN, SUSPEND_TASK_AFTER_NUM_FAILURES, COMMENT, or AS in CREATE TASK",
61016106
self.peek_token(),
61026107
);
61036108
}

0 commit comments

Comments
 (0)