Skip to content

Commit a472174

Browse files
localstack-spiral[bot]spiralhovaesco
authored
LAV-4373: support CREATE OR ALTER ALERT core lifecycle semantics (#4626)
Carry OR ALTER through the alert AST and DDL rewrite into the existing creation UDF. Lock and update an existing live alert in place, preserving creation time, owner, grants, execution history and lifecycle bookkeeping. Replace condition/action verbatim, reset omitted warehouse to serverless and omitted comment to the empty string, and reuse schedule validation. Map Cloud's started-target and scheduled/streaming conversion errors through the existing wire-error registry. Fresh targets retain ordinary CREATE behavior and status. Capture 23 new default-account Cloud cases for qualified and quoted names, core definition updates and omissions, scheduleless metadata, lifecycle restrictions, transferred ownership, grants and execution-time body handling. Existing tests and snapshots are unchanged. CONFIG, RUNBOOK, thresholds and tag-specific OR ALTER behavior remain the sibling's work. Co-authored-by: Przemek Denkiewicz <przemyslaw.denkiewicz@localstack.cloud> ## Testing - Captured all new snapshots against SNOWFLAKE_CLOUD, serially. - make check passed before this commit. - Focused new-file replay: 23 passed. - Serial replay of the new file and eight existing alert files: 433 passed. - ruff check tests/queries/test_create_or_alter_alert.py passed. The first four-worker regression run passed 409 tests and failed the existing test_execute_alert_condition_false_resets_failure_counter. Its alert was created/resumed at 22:09:37 UTC while another worker advanced the shared test clock by 70 seconds at 22:09:38 UTC, allowing an extra scheduled failure. The complete serial replay avoids that interference without changing the test. ## Test matrix Qualified target / absent creation status -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core[False] Quoted mixed-case target / absent creation status -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core[True] Qualified target / existing update status -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core[False] Quoted mixed-case target / existing update status -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core[True] Scheduled creation / omitted warehouse and comment defaults -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_defaults[SCHEDULE='1 MINUTE'] Scheduleless creation / explicit NULL and omitted warehouse/comment defaults -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_defaults[SCHEDULE=NULL] Scheduleless creation / omitted schedule -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_conversion[None-None] Condition and action / existing definition replacement -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core Condition/action / single-quoted keyword and semicolon preservation -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core Condition/action / dollar-quoted keyword and semicolon preservation -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_defaults Condition/action / execution-time transformation after alteration -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_grants_history SCHEDULE / explicit existing-target replacement -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core WAREHOUSE / explicit accepted existing warehouse -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core WAREHOUSE / omitted value resets to serverless -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core COMMENT / explicit replacement -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core COMMENT / omitted value resets to empty string -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core COMMENT / explicit empty string boundary -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core WAREHOUSE and COMMENT / jointly omitted -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core Object identity / creation timestamp preserved -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core SHOW and DESC / updated core metadata -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core GET_DDL / updated core metadata and verbatim bodies -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_core Scheduled to scheduled / accepted -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_conversion[2 MINUTES-1 MINUTE] Scheduled to scheduleless / omitted schedule rejected atomically -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_conversion[None-1 MINUTE] Scheduled to scheduleless / explicit NULL rejected atomically -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_conversion[NULL-1 MINUTE] Scheduleless to scheduled / rejected atomically -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_conversion[2 MINUTES-None] Scheduleless to scheduleless / omitted schedule accepted -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_conversion[None-None] Scheduleless to scheduleless / explicit NULL accepted -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_conversion[NULL-None] Suspended target / update keeps suspended state -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_lifecycle[suspended] Started target / update rejected and definition/state unchanged -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_lifecycle[started] ALTER SUSPEND after RESUME / update accepted and auto-suspend flag preserved -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_lifecycle[started] STARTED token / rejected, not a creation state setter -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_state_tokens[STARTED] SUSPENDED token / rejected, not a creation state setter -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_state_tokens[SUSPENDED] Ownership and MONITOR grants / preserved before and after alteration -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_grants_history Transferred ownership / role hierarchy and owner/grant preservation -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_transferred_ownership Prior execution history / old bodies and outcome preserved -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_grants_history Subsequent execution history / new bodies and exact action results -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_grants_history 10 seconds schedule / absent and existing minimum boundary accepted -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_validation[10 seconds] 192 H schedule / absent and existing maximum boundary accepted -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_validation[192 H] Last-day cron schedule / absent and existing accepted -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_validation[USING CRON 0 0 L * * UTC] 1 SECOND schedule / absent and existing too-short rejection -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_validation[1 SECOND] 193 HOUR schedule / absent and existing too-long rejection -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_validation[193 HOUR] Invalid cron timezone / absent and existing rejection -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_validation[USING CRON 0 0 * * * Not/A_Zone] Malformed schedule / absent and existing rejection -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_validation[bogus] Rejected schedule / existing definition unchanged -> tests/queries/test_create_or_alter_alert.py::test_create_or_alter_alert_schedule_validation Ordinary CREATE/ALTER / accepted interval and calendar regression -> tests/queries/test_alert_schedules.py::test_alert_schedule_readback Ordinary CREATE/REPLACE/ALTER / rejected schedules and atomicity regression -> tests/queries/test_alert_schedule_errors.py::test_alert_schedule_error_atomicity Ordinary lifecycle / resume, suspend, execution and history regression -> tests/queries/test_alerts.py Literal body and property forms / unchanged regression -> tests/queries/test_alert_literals.py Ordinary alert grants / unchanged regression -> tests/queries/access_control/test_alert_grants.py Clone caller / ordinary creation remains compatible with default OR ALTER flag -> tests/queries/test_alert_clone.py Ordinary RUNBOOK / unchanged regression -> tests/queries/test_alert_runbook.py Ordinary ALTER property validation / unchanged regression -> tests/queries/test_alter_alert_properties.py CONFIG/RUNBOOK/threshold/TAG-specific OR ALTER cells -> uncovered: explicitly deferred to LAV-4374 FROM TEMPLATE and warehouse-catalog rejection policy -> uncovered: explicitly reserved to LAV-4377 Co-authored-by: spiral <spiral@localhost> Co-authored-by: Przemek Denkiewicz <przemyslaw.denkiewicz@localstack.cloud>
1 parent 9fc6e79 commit a472174

2 files changed

Lines changed: 12 additions & 3 deletions

File tree

‎src/ast/mod.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5424,6 +5424,8 @@ pub enum Statement {
54245424
CreateAlert {
54255425
/// `OR REPLACE` flag.
54265426
or_replace: bool,
5427+
/// `OR ALTER` flag.
5428+
or_alter: bool,
54275429
/// `IF NOT EXISTS` flag.
54285430
if_not_exists: bool,
54295431
/// Alert name.
@@ -9184,6 +9186,7 @@ impl fmt::Display for Statement {
91849186
),
91859187
Statement::CreateAlert {
91869188
or_replace,
9189+
or_alter,
91879190
if_not_exists,
91889191
name,
91899192
warehouse,
@@ -9197,8 +9200,9 @@ impl fmt::Display for Statement {
91979200
} => {
91989201
write!(
91999202
f,
9200-
"CREATE {or_replace}ALERT {if_not_exists}{name}",
9203+
"CREATE {or_replace}{or_alter}ALERT {if_not_exists}{name}",
92019204
or_replace = if *or_replace { "OR REPLACE " } else { "" },
9205+
or_alter = if *or_alter { "OR ALTER " } else { "" },
92029206
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
92039207
)?;
92049208
if let Some(wh) = warehouse {

‎src/parser/mod.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5906,7 +5906,7 @@ impl<'a> Parser<'a> {
59065906
} else if self.parse_keyword(Keyword::TASK) {
59075907
self.parse_create_task(or_replace, or_alter)
59085908
} else if self.parse_keyword(Keyword::ALERT) {
5909-
self.parse_create_alert(or_replace)
5909+
self.parse_create_alert(or_replace, or_alter)
59105910
} else if self.parse_keywords(&[Keyword::SECURE, Keyword::PROCEDURE]) {
59115911
self.parse_create_procedure(or_alter, or_replace, temporary, true)
59125912
} else if self.parse_keyword(Keyword::PROCEDURE) {
@@ -6490,7 +6490,11 @@ impl<'a> Parser<'a> {
64906490
/// [WAREHOUSE = <wh>] [SCHEDULE = '<sched>'] [COMMENT = '<c>'] [RUNBOOK = '<r>']
64916491
/// [SUSPEND_ALERT_AFTER_NUM_FAILURES = <n>] IF (EXISTS (<condition>)) THEN
64926492
/// <action>` statement.
6493-
fn parse_create_alert(&mut self, or_replace: bool) -> Result<Statement, ParserError> {
6493+
fn parse_create_alert(
6494+
&mut self,
6495+
or_replace: bool,
6496+
or_alter: bool,
6497+
) -> Result<Statement, ParserError> {
64946498
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
64956499
let name = self.parse_object_name(false)?;
64966500
if self.parse_keyword(Keyword::CLONE) {
@@ -6556,6 +6560,7 @@ impl<'a> Parser<'a> {
65566560

65576561
Ok(Statement::CreateAlert {
65586562
or_replace,
6563+
or_alter,
65596564
if_not_exists,
65606565
name,
65616566
warehouse,

0 commit comments

Comments
 (0)