Réparer maintenant
- Déposez le fichier
- La réparation s'exécute en local
- Téléchargez le résultat
Quand SQLite — ou tout ce qui repose dessus : l'historique d'un navigateur, la sauvegarde d'un téléphone, la base de données locale d'une application de messagerie, les réglages d'une application de bureau — signale "database disk image is malformed", il s'est heurté à SQLITE_CORRUPT (code de résultat 11). Il parcourait le b-tree qui indexe vos tables et a atterri sur une page dont l'octet de type, les pointeurs de cellule ou l'en-tête d'enregistrement contredisent le format ; il s'arrête donc plutôt que de renvoyer n'importe quoi. Déposez le fichier .db, .sqlite ou .sqlite3 ci-dessus et l'outil le lit page par page dans votre navigateur, suit la structure de b-tree qui subsiste, balaie le reste avec une analyse sans pointeurs et écrit chaque ligne décodable dans une base de données neuve et ouvrable — sans que le fichier quitte jamais votre machine.
Ce que "malformed" signifie au niveau de la page
Une base de données SQLite est un fichier unique découpé en pages de taille fixe — une puissance de deux comprise entre 512 et 65536 octets, stockée sous forme d'une valeur de 16 bits à l'offset 16 de l'en-tête (la valeur sur disque 1 est la façon qu'a la spécification de représenter 65536). Tout fichier commence par la chaîne magique de 16 octets SQLite format 3\0, suivie d'un en-tête de 100 octets qui consigne la taille de page, l'encodage de texte à l'offset 56 (1 = UTF-8, 2 = UTF-16le, 3 = UTF-16be), le nombre de pages déclaré à l'offset 28 et la liste des pages libres (freelist). Si la taille de page est mal lue, chaque page située après la première tombe au mauvais offset — et c'est l'une des façons dont un fichier devient "malformed" sans qu'une seule ligne soit touchée.
À l'intérieur, les données vivent dans des b-trees. Chaque page commence par un octet de type : 0x0d pour une feuille de table (qui contient les lignes réelles), 0x05 pour une page interne de table (qui ne contient que des pointeurs vers des pages filles) et 0x0a/0x02 pour les équivalents d'index. La page 1 est particulière — son en-tête de b-tree se place après l'en-tête de base de données de 100 octets — et elle est la racine de sqlite_master, la table de schéma dont les lignes sont (type, name, tbl_name, rootpage, sql). Pour lire l'une de vos tables, SQLite y cherche sa rootpage, puis descend par les pages internes jusqu'aux feuilles, en lisant dans chaque cellule la longueur de la charge utile (varint), le rowid (varint) et le corps de l'enregistrement.
Comme une requête parcourt cet arbre, un seul lien rompu empoisonne tout ce qui se trouve en dessous. Une page interne mise à zéro, un pointeur de cellule qui vise au-delà de la fin de la page, un pointeur d'extrême droite qui reboucle sur lui-même, ou un en-tête d'enregistrement dont les types série ne concordent pas — n'importe lequel de ces cas fait lever à SQLite "database disk image is malformed" dès qu'il marche sur les dégâts. Les lignes des pages feuilles saines sont intactes ; SQLite ne peut simplement pas les atteindre à travers un index rompu. PRAGMA integrity_check signale la même catégorie de défaut.
Comment un parcours brut des pages sauve vos lignes
La solution imite ce que fait la commande .recover du shell de SQLite lui-même, mais s'exécute entièrement dans le navigateur. D'abord l'outil lit sqlite_master depuis la page 1 pour connaître le nom de chaque table, ses colonnes (extraites du SQL de son CREATE TABLE) et sa rootpage, puis parcourt chaque b-tree depuis cette racine — la voie rapide et exacte quand les pointeurs sont intacts. Quand un parcours rencontre une page manquante ou invalide, il se dégrade en "incomplet" au lieu d'échouer, en conservant toutes les lignes qu'il a réunies jusqu'à la rupture.
Vient ensuite la partie qui surpasse une simple réouverture : un balayage sans pointeurs. Le scanner ignore tous les pointeurs et traite chaque page feuille de table (type 0x0d) du fichier comme un sac d'enregistrements, en décodant chaque cellule directement à partir de ses octets — la longueur de l'en-tête (varint), le tableau des types série, puis chaque valeur (le type série 0 est NULL, 8/9 sont les littéraux 0 et 1, 7 est un float64, et les codes pairs/impairs au-dessus de 12 sont des BLOB/TEXT). Les chaînes de débordement (overflow) des grandes valeurs sont suivies de page en page. C'est exactement ainsi que les lignes survivent à une page interne mise à zéro, à une page dans la freelist ou à une table supprimée : la feuille contient toujours les données même quand plus rien ne pointe vers elle. Les lignes dont le nombre de colonnes correspond sans ambiguïté à une table connue y sont réinsérées ; celles qui sont vraiment inattribuables sont écrites dans une table lost_and_found avec les colonnes pgno, cellidx, nfield, rowid et c0…cN, de sorte que rien de décodable ne soit jeté.
Enfin l'outil construit une base de données neuve avec sqlite3.wasm (chargé depuis la même origine dans /engines/sqlite/), en émettant un CREATE TABLE permissif pour chaque table — les noms de colonnes d'origine et tout INTEGER PRIMARY KEY sont conservés pour que l'alias du rowid soit préservé, mais sans contraintes NOT NULL/UNIQUE/CHECK/clé étrangère qui rejetteraient une ligne ressuscitée — puis fait un INSERT OR IGNORE de chaque ligne récupérée et exporte le résultat sous forme de .db propre. Si un fichier frère -wal (journal d'écriture anticipée) est fourni, ses trames validées sont superposées d'abord, de sorte que la récupération reflète la dernière transaction validée.
Où la donnée s'arrête vraiment — et pourquoi rien n'est téléversé
La récupération atteint ce qui est présent ; elle ne peut pas inventer ce que le disque ne conserve plus. Si le fichier a été tronqué — une copie ou une synchronisation qui s'est arrêtée trop tôt, laissant une taille qui n'est pas un multiple exact de la taille de page — toute page située après la coupure a physiquement disparu, et vous obtenez les lignes qui ont été écrites intégralement avant elle. Les cellules qui se trouvent sur des pages intactes mais dont l'en-tête d'enregistrement est incohérent avec lui-même (le classique bit-rot) sont écartées et comptabilisées, plutôt que devinées. Une base de données chiffrée (SQLCipher ou l'extension SEE) n'a pas de magie en clair — sa première page est du texte chiffré — de sorte que sans la clé il n'y a rien à parcourir, et la récupération n'est pas tentée.
Deux notes d'honnêteté sur lesquelles SQLite lui-même insiste. Les lignes supprimées peuvent réapparaître : le balayage sans pointeurs lit les pages de la freelist et les pages non compactées (vacuum), si bien que des enregistrements que vous croyiez disparus peuvent resurgir dans le résultat. Et les valeurs récupérées reflètent les classes de stockage sur disque, non les affinités de colonne déclarées — le type apparent d'une colonne peut changer. La propre consigne de SQLite est sans détour : les données extraites d'une base de données corrompue sont toujours suspectes. Vérifiez-les par rapport à une source fiable avant de vous y fier.
Tout cela s'exécute dans l'onglet de votre navigateur — le scanner est du TypeScript sans dépendances et le module d'écriture est du WASM, il n'y a donc aucun aller-retour vers un serveur. Cela compte quand la base de données est un gestionnaire de mots de passe, un historique de discussion, des données de santé ou le magasin privé d'une application : vous pouvez ouvrir l'onglet Réseau et confirmer que 0 octet du fichier ne quitte jamais votre machine.
Ce que cela peut et ne peut pas réparer
Peut réparer
- Bases de données qui lèvent SQLITE_CORRUPT / "database disk image is malformed" à cause d'une page ou d'un pointeur de b-tree endommagé
- Lignes sur des pages feuilles de table saines qu'une page interne rompue ou une rootpage défectueuse a rendues inaccessibles
- Lignes sur des pages internes mises à zéro, des pages dans la freelist ou des tables supprimées, récupérées par le balayage des feuilles sans pointeurs
- Grandes valeurs qui ont débordé sur des chaînes de pages de débordement (overflow), réassemblées pendant le parcours
- Modifications sans checkpoint dans un fichier frère -wal, superposées avant l'analyse
Ne peut pas réparer
- Lignes situées après la coupure dans un fichier tronqué — ces pages ont physiquement disparu
- Cellules dont l'en-tête d'enregistrement est trop corrompu pour être décodé (bit-rot) — elles sont écartées, pas devinées
- Bases de données chiffrées SQLCipher/SEE sans la clé (les pages sont du texte chiffré)
- Les garanties d'origine UNIQUE/CHECK/clé étrangère — la récupération relâche les contraintes pour que les lignes ressuscitées ne soient pas rejetées
- Un fichier de 0 octet ou sans la magie SQLite ni pages feuilles décodables
Si une réparation échoue, nous vous expliquons pourquoi (données manquantes ou structure endommagée), et vous n'êtes jamais facturé pour une réparation échouée.