use rusqlite::{Connection, Result}; /// Aplica todas as migrations necessárias para atualizar o banco /// para a versão mais recente. pub fn aplicar_migrations(conn: &Connection) -> Result<()> { // Criar tabela de controle de versão se não existir conn.execute_batch( "CREATE TABLE IF NOT EXISTS schema_version ( versao INTEGER NOT NULL );", )?; let versao_atual: i64 = conn .query_row("SELECT versao FROM schema_version LIMIT 1;", [], |row| { row.get(0) }) .unwrap_or(0); if versao_atual < 1 { migration_v1(conn)?; } if versao_atual < 2 { migration_v2(conn)?; } if versao_atual < 3 { migration_v3(conn)?; } Ok(()) } /// Migration v3: adicionar colunas de tipo de documento nos layouts. fn migration_v3(conn: &Connection) -> Result<()> { conn.execute_batch( "BEGIN; ALTER TABLE layouts ADD COLUMN indice_documento_tipo INTEGER; ALTER TABLE layouts ADD COLUMN pos_documento_tipo TEXT; UPDATE schema_version SET versao = 3; COMMIT;", )?; Ok(()) } /// Migration v2: adicionar índice único em layouts.nome. /// /// Renomeia duplicatas (sufixo com id) antes de criar o índice para não falhar /// em bancos que já possuem nomes repetidos. fn migration_v2(conn: &Connection) -> Result<()> { conn.execute_batch( "BEGIN; UPDATE layouts SET nome = nome || ' (' || id || ')' WHERE id NOT IN ( SELECT MIN(id) FROM layouts GROUP BY nome ); CREATE UNIQUE INDEX IF NOT EXISTS idx_layouts_nome ON layouts (nome); UPDATE schema_version SET versao = 2; COMMIT;", )?; Ok(()) } /// Migration v1: criar tabela de layouts. fn migration_v1(conn: &Connection) -> Result<()> { conn.execute_batch( "BEGIN; CREATE TABLE IF NOT EXISTS layouts ( id INTEGER PRIMARY KEY AUTOINCREMENT, nome TEXT NOT NULL, tipo TEXT NOT NULL CHECK(tipo IN ('csv', 'xlsx')), -- Campos CSV delimitador TEXT, encoding TEXT, linha_cabecalho INTEGER, indice_numero INTEGER, indice_serie INTEGER, indice_valor INTEGER, indice_data INTEGER, -- Campos XLSX aba TEXT, pos_numero TEXT, pos_serie TEXT, pos_valor TEXT, pos_data TEXT ); INSERT INTO schema_version (versao) VALUES (1); COMMIT;", )?; Ok(()) }