| 60 | } |
| 61 | |
| 62 | fn match_restore(sql: &str, upper: &str) -> Option<CopyIntent> { |
| 63 | // Pattern: COPY tenant_restore ( <id> ) FROM STDIN [ DRY RUN ] |
| 64 | let after_copy = upper.strip_prefix("COPY")?.trim_start(); |
| 65 | let after_fn = after_copy.strip_prefix("TENANT_RESTORE")?.trim_start(); |
| 66 | let after_paren = after_fn.strip_prefix('(')?.trim_start(); |
| 67 | |
| 68 | let close_idx = after_paren.find(')')?; |
| 69 | let id_token = after_paren[..close_idx].trim(); |
| 70 | let tenant_id: u64 = id_token.parse().ok()?; |
| 71 | |
| 72 | let after_close = after_paren[close_idx + 1..].trim_start(); |
| 73 | let after_from = after_close.strip_prefix("FROM")?.trim_start(); |
| 74 | let after_stdin = after_from.strip_prefix("STDIN")?.trim_start(); |
| 75 | |
| 76 | let dry_run = match after_stdin { |
| 77 | "" => false, |
| 78 | rest => { |
| 79 | // Accept "DRY RUN" or "DRYRUN" (matches the existing legacy parser). |
| 80 | let rest = rest.trim(); |
| 81 | if rest == "DRY RUN" || rest == "DRYRUN" { |
| 82 | true |
| 83 | } else { |
| 84 | return None; |
| 85 | } |
| 86 | } |
| 87 | }; |
| 88 | |
| 89 | let _ = sql; |
| 90 | Some(CopyIntent::RestoreTenant { tenant_id, dry_run }) |
| 91 | } |
| 92 | |
| 93 | #[cfg(test)] |
| 94 | mod tests { |