#!/usr/bin/env bash # backup_sqlite_db — Snapshot atomico de una BD SQLite usando VACUUM INTO. # Mas seguro que cp: no corrompe si hay escrituras concurrentes. backup_sqlite_db() { local source="$1" local dest="$2" # Verificar dependencia sqlite3 if ! command -v sqlite3 &>/dev/null; then echo "backup_sqlite_db: sqlite3 no encontrado en PATH" >&2 return 5 fi # Verificar que source existe if [[ ! -f "$source" ]]; then echo "backup_sqlite_db: source no existe: $source" >&2 return 1 fi # Verificar que source es SQLite valido (header magico) local header header=$(head -c 16 "$source" 2>/dev/null | strings | head -n1) if [[ "$header" != "SQLite format 3" ]]; then echo "backup_sqlite_db: source no es una BD SQLite valida: $source" >&2 return 2 fi # Crear directorio destino si no existe local dest_dir dest_dir=$(dirname "$dest") if [[ ! -d "$dest_dir" ]]; then mkdir -p "$dest_dir" || { echo "backup_sqlite_db: no se pudo crear directorio: $dest_dir" >&2 return 3 } fi # Si el destino existe, borrarlo para que VACUUM INTO no falle if [[ -f "$dest" ]]; then rm -f "$dest" fi # Ejecutar VACUUM INTO (escape de comillas simples en el path) local escaped_dest="${dest//\'/\'\'}" if ! sqlite3 "$source" "VACUUM INTO '${escaped_dest}';" 2>/dev/null; then echo "backup_sqlite_db: fallo en VACUUM INTO: source=$source dest=$dest" >&2 return 3 fi # Verificar que dest existe y tiene tamaño > 0 if [[ ! -f "$dest" ]]; then echo "backup_sqlite_db: dest no fue creado: $dest" >&2 return 3 fi local bytes bytes=$(wc -c < "$dest" 2>/dev/null) if [[ -z "$bytes" || "$bytes" -eq 0 ]]; then echo "backup_sqlite_db: dest tiene tamaño 0: $dest" >&2 return 4 fi echo "OK ${bytes} bytes -> ${dest}" return 0 }