Corruption d'Index SQLite FTS5 à partir de Données Erronées
Problème
SQLite retourne « database disk image is malformed » (code d'erreur 11) lors de la tentative d'UPDATE ou INSERT sur des enregistrements, même si PRAGMA integrity_check rapporte que la base de données est « ok ». L'erreur n'affecte que certains enregistrements, pas toutes les écritures.
Contexte / Conditions de Déclenchement
- Message d'erreur :
Error: stepping, database disk image is malformed (11) PRAGMA integrity_checkretourneok(trompeur !)- Les requêtes SELECT sur les mêmes enregistrements fonctionnent correctement
- Seuls certains enregistrements ne peuvent pas être mis à jour ; d'autres enregistrements de la même table se mettent à jour avec succès
- Les enregistrements affectés contiennent du texte avec des tabulations incorporées (
\t), des sauts de ligne (\n), ou d'autres caractères de contrôle dans des colonnes indexées par FTS5 - Un processus concurrent (comme un serveur Node.js) peut avoir la base de données ouverte
Solution
-
Identifier les tables FTS5 liées à la table affectée :
SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%fts%'; -
Arrêter tous les processus maintenant la base de données ouverte (la reconstruction nécessite un accès exclusif) :
kill $(lsof -ti :PORT) # Tuer le processus serveur -
Reconstruire l'index FTS5 :
INSERT INTO table_fts(table_fts) VALUES('rebuild');Remplacer
table_ftspar le nom réel de votre table FTS5 (par ex.,people_fts). -
Réessayer l'opération échouée - elle devrait maintenant réussir.
-
Redémarrer votre serveur d'application.
Vérification
- La requête UPDATE/INSERT précédemment échouée réussit maintenant
- Les requêtes de recherche en texte intégral retournent toujours les résultats corrects
- Plus d'erreurs « malformed » lors des écritures ultérieures
Exemple
# L'erreur se produit :
sqlite3 data/tracker.db "UPDATE people SET name = 'More' WHERE id = 11;"
# Error: stepping, database disk image is malformed (11)
# Mais le contrôle d'intégrité réussit :
sqlite3 data/tracker.db "PRAGMA integrity_check;"
# ok
# Et SELECT fonctionne bien :
sqlite3 data/tracker.db "SELECT id, name FROM people WHERE id = 11;"
# 11|More Masajes\t\n\t\tEscort 23 años...
# Correction : Arrêter le serveur, reconstruire l'index FTS5
kill $(lsof -ti :3001)
sqlite3 data/tracker.db "INSERT INTO people_fts(people_fts) VALUES('rebuild');"
# Maintenant la mise à jour fonctionne :
sqlite3 data/tracker.db "UPDATE people SET name = 'More' WHERE id = 11;"
# Success!
Prévention
- Nettoyer le texte avant insertion dans des colonnes indexées par FTS5 :
const cleanName = rawName.replace(/[\t\n\r]+/g, ' ').trim(); - Supprimer les caractères de contrôle des données scrappées avant insertion en base de données
- Envisager l'utilisation de tables FTS5
content=(contenu externe) qui peuvent être reconstruites indépendamment sans affecter la table principale
Remarques
- Le code d'erreur 11 (
SQLITE_CORRUPT) est identique à une corruption réelle du disque, ce qui rend ce problème difficile à diagnostiquer - La corruption se trouve dans les tables fantômes FTS5 (
_data,_idx,_docsize), pas dans la table principale, ce qui explique pourquoiintegrity_checkréussit - Si
rebuildéchoue avec « database is locked », assurez-vous qu'aucun autre processus n'a la base de données ouverte (vérifier aveclsofoufuser) - Cela peut également survenir quand des enregistrements sont supprimés ou mis à jour de l'extérieur (par ex., via sqlite3 CLI) tandis qu'une connexion en mode WAL est active