Structural extraction for `CREATE SCHEDULE`. Extracts name, cron_expr, body_sql, scope, missed_policy, allow_overlap as primitive types. The handler converts scope/missed_policy strings to their respective enum variants.
(upper: &str, trimmed: &str)
| 91 | /// as primitive types. The handler converts scope/missed_policy strings to |
| 92 | /// their respective enum variants. |
| 93 | fn parse_create_schedule(upper: &str, trimmed: &str) -> NodedbStatement { |
| 94 | let prefix = "CREATE SCHEDULE "; |
| 95 | let rest = &trimmed[prefix.len()..]; |
| 96 | let _rest_upper = &upper[prefix.len()..]; |
| 97 | let tokens: Vec<&str> = rest.split_whitespace().collect(); |
| 98 | |
| 99 | let name = tokens.first().map(|s| s.to_lowercase()).unwrap_or_default(); |
| 100 | |
| 101 | // Extract cron expression (quoted or 5-field unquoted). |
| 102 | let cron_expr = extract_cron_expr_str(rest).unwrap_or_default(); |
| 103 | |
| 104 | // scope: SCOPE LOCAL → "LOCAL", default "NORMAL" |
| 105 | let scope = if upper.contains(" SCOPE LOCAL") { |
| 106 | "LOCAL".to_string() |
| 107 | } else { |
| 108 | "NORMAL".to_string() |
| 109 | }; |
| 110 | |
| 111 | // allow_overlap: default true; WITH (ALLOW_OVERLAP = false) → false |
| 112 | let mut allow_overlap = true; |
| 113 | let mut missed_policy = "SKIP".to_string(); |
| 114 | |
| 115 | if let Some(with_pos) = upper.find(" WITH ") { |
| 116 | let after_with = &trimmed[with_pos + 6..]; |
| 117 | if let Some(inner) = after_with |
| 118 | .trim() |
| 119 | .strip_prefix('(') |
| 120 | .and_then(|s| s.split_once(')')) |
| 121 | .map(|(i, _)| i) |
| 122 | { |
| 123 | for opt in inner.split(',') { |
| 124 | let opt = opt.trim(); |
| 125 | if let Some((key, val)) = opt.split_once('=') { |
| 126 | let key = key.trim().to_uppercase(); |
| 127 | let val = val.trim().trim_matches('\'').trim_matches('"'); |
| 128 | match key.as_str() { |
| 129 | "ALLOW_OVERLAP" => { |
| 130 | allow_overlap = !val.eq_ignore_ascii_case("false"); |
| 131 | } |
| 132 | "MISSED" => { |
| 133 | missed_policy = val.to_uppercase(); |
| 134 | } |
| 135 | _ => {} |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | // Body SQL: everything after the last " AS " keyword. |
| 143 | let body_sql = upper |
| 144 | .rfind(" AS ") |
| 145 | .map(|pos| trimmed[pos + 4..].trim().to_string()) |
| 146 | .unwrap_or_default(); |
| 147 | |
| 148 | NodedbStatement::Automation(AutomationStmt::CreateSchedule { |
| 149 | name, |
| 150 | cron_expr, |