fix: SQL trigger-aware migration splitter, SpeechModel re-export, i64 type alignment
crates/storage/src/migrations.rs:
Replace naive sql.split(';') with split_statements() that tracks
BEGIN...END depth, so migrations containing trigger definitions
execute correctly instead of being split mid-block.
crates/transcription/src/lib.rs:
Re-export transcribe_rs::SpeechModel so callers in src-tauri can
reference it without adding transcribe-rs as a direct dependency.
src-tauri/src/commands/models.rs:
Use Box<dyn SpeechModel + Send> as the load_model_from_disk return
type, matching the trait object that transcription crates produce.
src-tauri/src/commands/transcripts.rs:
Remove the stale .map(|v| v as i32) casts on sample_rate and
audio_channels. InsertTranscriptParams now stores these as i64
(ebf449b), matching the i64 fields on CreateTranscriptRequest;
casting to i32 first would silently truncate large sample rates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,40 @@ const MIGRATIONS: &[(i64, &str, &str)] = &[
|
||||
"#),
|
||||
];
|
||||
|
||||
/// Split SQL into individual statements, respecting BEGIN...END trigger blocks.
|
||||
fn split_statements(sql: &str) -> Vec<String> {
|
||||
let mut result = Vec::new();
|
||||
let mut current = String::new();
|
||||
let mut depth: i32 = 0;
|
||||
let upper = sql.to_ascii_uppercase();
|
||||
let ub = upper.as_bytes();
|
||||
let ob = sql.as_bytes();
|
||||
let n = ob.len();
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
let prev_alpha = i > 0 && ob[i - 1].is_ascii_alphanumeric();
|
||||
if !prev_alpha && ub[i..].starts_with(b"BEGIN") {
|
||||
let next_alpha = i + 5 < n && ob[i + 5].is_ascii_alphanumeric();
|
||||
if !next_alpha { depth += 1; }
|
||||
}
|
||||
if !prev_alpha && ub[i..].starts_with(b"END") {
|
||||
let next_alpha = i + 3 < n && ob[i + 3].is_ascii_alphanumeric();
|
||||
if !next_alpha && depth > 0 { depth -= 1; }
|
||||
}
|
||||
if ob[i] == b';' && depth == 0 {
|
||||
let stmt = current.trim().to_string();
|
||||
if !stmt.is_empty() { result.push(stmt); }
|
||||
current = String::new();
|
||||
} else {
|
||||
current.push(ob[i] as char);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
let stmt = current.trim().to_string();
|
||||
if !stmt.is_empty() { result.push(stmt); }
|
||||
result
|
||||
}
|
||||
|
||||
/// Ensure the schema_version table exists and run any pending migrations.
|
||||
pub async fn run_migrations(pool: &SqlitePool) -> Result<()> {
|
||||
sqlx::query(
|
||||
@@ -132,12 +166,9 @@ pub async fn run_migrations(pool: &SqlitePool) -> Result<()> {
|
||||
if *version > current {
|
||||
log::info!("Running migration {}: {}", version, description);
|
||||
|
||||
let statements: Vec<&str> = sql.split(';')
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
let statements = split_statements(sql);
|
||||
|
||||
for statement in statements {
|
||||
for statement in &statements {
|
||||
sqlx::query(statement)
|
||||
.execute(pool)
|
||||
.await
|
||||
|
||||
@@ -6,6 +6,7 @@ pub use concurrency::run_inference;
|
||||
pub use local_engine::{
|
||||
load_parakeet, load_whisper, LocalEngine, TimedTranscript,
|
||||
};
|
||||
pub use transcribe_rs::SpeechModel;
|
||||
pub use model_manager::{
|
||||
download, is_downloaded, list_downloaded, model_dir, models_dir,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user