"Database disk image is malformed"

È il codice di errore 11, SQLITE_CORRUPT: mentre percorreva un b-tree, SQLite è arrivato a una pagina o a un puntatore che non ha più senso, quindi si rifiuta di fidarsi del file. Le righe in sé quasi sempre sono ancora sul disco — a rompersi è la mappa che porta a esse — e raggiungerle non richiede di inviare il tuo database da nessuna parte.

Il tuo file non lascia mai il tuo dispositivo — la riparazione avviene nel browser. 0 byte caricati.

Your data is the big block — usually intact. What breaks is the small index. Repair rebuilds it.

Drop a file here or browseTap to choose a file

ZIP · Office · PDF · video · JPG · PNG · RAR/7z · SQLite — repaired right here in your browser. Nothing is uploaded

0 bytes uploadedFree: 3 repairs/day — up to 500 MB video, 100 MB docs & archives, 50 MB photos. Free download. Single repair €5.90. No account required.

Ripara ora

  1. Trascina il file
  2. La riparazione avviene in locale
  3. Scarica il risultato

Quando SQLite — o qualsiasi cosa costruita su di esso: la cronologia di un browser, il backup di uno smartphone, il database locale di un'app di messaggistica, le impostazioni di un'applicazione desktop — segnala "database disk image is malformed", ha incontrato SQLITE_CORRUPT (codice di risultato 11). Stava percorrendo il b-tree che indicizza le tue tabelle ed è atterrato su una pagina il cui byte di tipo, i puntatori di cella o l'intestazione del record contraddicono il formato, quindi si ferma invece di restituire spazzatura. Trascina qui sopra il file .db, .sqlite o .sqlite3 e lo strumento lo legge pagina per pagina nel tuo browser, segue la struttura di b-tree che sopravvive, spazza via il resto con una scansione senza puntatori e scrive ogni riga decodificabile in un database nuovo e apribile — senza che il file lasci mai il tuo dispositivo.

Cosa significa "malformed" a livello di pagina

Un database SQLite è un unico file suddiviso in pagine di dimensione fissa — una potenza di due tra 512 e 65536 byte, memorizzata come valore a 16 bit all'offset 16 dell'intestazione (il valore su disco 1 è il modo in cui la specifica rappresenta 65536). Ogni file inizia con la stringa magica di 16 byte SQLite format 3\0, seguita da un'intestazione di 100 byte che registra la dimensione di pagina, la codifica del testo all'offset 56 (1 = UTF-8, 2 = UTF-16le, 3 = UTF-16be), il numero di pagine dichiarato all'offset 28 e la lista dei liberi (freelist). Se la dimensione di pagina viene letta male, ogni pagina successiva alla prima cade all'offset sbagliato — ed è uno dei modi in cui un file diventa "malformed" senza che venga toccata una sola riga.

All'interno, i dati vivono in b-tree. Ogni pagina inizia con un byte di tipo: 0x0d per una foglia di tabella (che contiene le righe vere e proprie), 0x05 per una interna di tabella (che contiene solo puntatori a pagine figlie) e 0x0a/0x02 per gli equivalenti dell'indice. La pagina 1 è speciale — la sua intestazione di b-tree si trova dopo l'intestazione di database di 100 byte — ed è la radice di sqlite_master, la tabella di schema le cui righe sono (type, name, tbl_name, rootpage, sql). Per leggere una delle tue tabelle, SQLite cerca lì la sua rootpage, poi scende attraverso le pagine interne fino alle foglie, leggendo da ogni cella la lunghezza del payload (varint), il rowid (varint) e il corpo del record.

Poiché una query attraversa questo albero, un solo collegamento rotto avvelena tutto ciò che sta al di sotto. Una pagina interna azzerata, un puntatore di cella che punta oltre la fine della pagina, un puntatore all'estremità destra che forma un ciclo, o un'intestazione di record i cui tipi seriali non tornano — ognuno di questi fa sì che SQLite sollevi "database disk image is malformed" nel momento in cui calpesta il danno. Le righe nelle pagine foglia sane sono intatte; SQLite semplicemente non riesce a raggiungerle attraverso un indice rotto. PRAGMA integrity_check segnala questa stessa classe di guasto.

Come un percorso grezzo delle pagine recupera le tue righe

La soluzione imita ciò che fa il comando .recover della shell di SQLite, ma viene eseguita interamente nel browser. Per prima cosa lo strumento legge sqlite_master dalla pagina 1 per conoscere il nome di ogni tabella, le sue colonne (estratte dal SQL del suo CREATE TABLE) e la sua rootpage, poi percorre ogni b-tree da quella radice — la via rapida ed esatta quando i puntatori sono intatti. Quando un percorso incontra una pagina mancante o non valida, degrada a "incompleto" invece di fallire, conservando tutte le righe raccolte fino alla rottura.

Poi arriva la parte che supera un semplice tentativo di riapertura: uno spazzamento senza puntatori. Lo scanner ignora tutti i puntatori e tratta ogni pagina foglia di tabella (tipo 0x0d) del file come un sacco di record, decodificando ogni cella direttamente dai suoi byte — la lunghezza dell'intestazione (varint), l'array dei tipi seriali e poi ogni valore (il tipo seriale 0 è NULL, 8/9 sono i letterali 0 e 1, 7 è un float64, e i codici pari/dispari sopra 12 sono BLOB/TEXT). Le catene di overflow dei valori grandi vengono seguite di pagina in pagina. È esattamente così che le righe sopravvivono a una pagina interna azzerata, a una pagina nella freelist o a una tabella eliminata: la foglia contiene ancora i dati anche quando nulla punta più a essa. Le righe il cui numero di colonne corrisponde in modo inequivocabile a una tabella nota vengono reinserite in essa; quelle davvero non attribuibili vengono scritte in una tabella lost_and_found con le colonne pgno, cellidx, nfield, rowid e c0…cN, così che nulla di decodificabile venga scartato.

Infine lo strumento costruisce un database nuovo con sqlite3.wasm (caricato dalla stessa origine da /engines/sqlite/), emettendo un CREATE TABLE permissivo per ogni tabella — i nomi originali delle colonne e qualsiasi INTEGER PRIMARY KEY vengono conservati affinché l'alias del rowid rimanga valido, ma senza i vincoli NOT NULL/UNIQUE/CHECK/chiave esterna che rifiuterebbero una riga resuscitata — poi esegue INSERT OR IGNORE di ogni riga recuperata ed esporta il risultato come un .db pulito. Se viene fornito un file fratello -wal (registro di scrittura anticipata, write-ahead log), i suoi frame confermati vengono sovrapposti per primi, così che il recupero rifletta l'ultima transazione confermata.

Dove il dato si ferma davvero — e perché non viene caricato nulla

Il recupero raggiunge ciò che è presente; non può inventare ciò che il disco non conserva più. Se il file è stato troncato — una copia o una sincronizzazione che si è interrotta troppo presto, lasciando una dimensione che non è un multiplo esatto della dimensione di pagina — ogni pagina successiva al taglio è fisicamente sparita, e ottieni le righe che erano state scritte per intero prima di esso. Le celle che si trovano su pagine intatte ma la cui intestazione di record è incoerente con se stessa (il classico bit-rot) vengono scartate e contate, invece di essere indovinate. Un database cifrato (SQLCipher o l'estensione SEE) non ha alcuna stringa magica in chiaro — la sua prima pagina è testo cifrato — quindi senza la chiave non c'è nulla da percorrere, e il recupero non viene tentato.

Due avvertenze di onestà su cui SQLite stesso insiste. Le righe eliminate possono riapparire: la scansione senza puntatori legge le pagine della freelist e quelle non compattate (vacuum), quindi record che credevi spariti possono riaffiorare nel risultato. E i valori recuperati riflettono le classi di memorizzazione su disco, non le affinità di colonna dichiarate — il tipo apparente di una colonna può cambiare. La guida di SQLite stessa è netta: i dati estratti da un database corrotto sono sempre sospetti. Verificali rispetto a una fonte attendibile prima di farci affidamento.

Tutto questo viene eseguito nella scheda del tuo browser — lo scanner è TypeScript senza dipendenze e lo scrittore è WASM, quindi non c'è alcun viaggio di andata e ritorno verso un server. Questo conta quando il database è un gestore di password, una cronologia di chat, dati sanitari o l'archivio privato di un'app: puoi aprire la scheda Rete e verificare che 0 byte del file lasciano il tuo dispositivo.

Cosa può e cosa non può riparare

Può riparare

  • Database che sollevano SQLITE_CORRUPT / "database disk image is malformed" a causa di una pagina o un puntatore di b-tree danneggiato
  • Righe su pagine foglia di tabella sane rese irraggiungibili da una pagina interna rotta o da una rootpage difettosa
  • Righe su pagine interne azzerate, pagine nella freelist o tabelle eliminate, recuperate dalla scansione delle foglie senza puntatori
  • Valori grandi traboccati in catene di pagine di overflow, riassemblati durante il percorso
  • Modifiche senza checkpoint in un file fratello -wal, sovrapposte prima della scansione

Non può riparare

  • Righe oltre il taglio in un file troncato — quelle pagine sono fisicamente sparite
  • Celle la cui intestazione di record è troppo corrotta per essere decodificata (bit-rot) — vengono scartate, non indovinate
  • Database cifrati SQLCipher/SEE senza la chiave (le pagine sono testo cifrato)
  • Le garanzie originali UNIQUE/CHECK/chiave esterna — il recupero allenta i vincoli affinché le righe resuscitate non vengano rifiutate
  • Un file di 0 byte oppure senza la stringa magica di SQLite e senza pagine foglia decodificabili

Se una riparazione fallisce, ti diciamo perché (dati mancanti rispetto a struttura danneggiata), e non ti viene mai addebitato nulla per una riparazione fallita.

Domande frequenti

"Database disk image is malformed" significa che i miei dati sono persi?

Di solito no. Significa che SQLite ha seguito il b-tree fino a una pagina o a un puntatore che viola il formato e si è fermato — codice di errore 11, SQLITE_CORRUPT. Le righe nelle pagine foglia sane sono ancora sul disco; sono solo irraggiungibili attraverso l'indice rotto. Leggere direttamente le pagine foglia e ricostruire un database nuovo ne recupera la maggior parte.

In che cosa differisce dal semplice riaprire il file o dall'eseguire PRAGMA integrity_check?

Riaprire fallisce nello stesso modo, e integrity_check si limita a segnalare il danno. Il recupero, invece, percorre il b-tree dove può e, soprattutto, esegue una scansione senza puntatori di ogni pagina foglia di tabella — così recupera righe su pagine a cui nulla punta più, cosa che un'apertura normale non può mai raggiungere.

Che cos'è la tabella lost_and_found nel database recuperato?

È il punto in cui vengono conservati, invece di essere scartati, i record che non è stato possibile associare a una tabella nota. Ciascuno viene memorizzato con il suo numero di pagina di origine (pgno), l'indice di cella, il numero di campi e il rowid, più i suoi valori di colonna grezzi come c0, c1 e così via — così puoi ispezionarli e ricollocarli a mano.

Perché sono comparse righe eliminate dopo il recupero?

La scansione senza puntatori legge le pagine della freelist e quelle non compattate (vacuum), dove SQLite lascia i vecchi record finché lo spazio non viene riutilizzato. Il recupero privilegia la completezza, quindi righe precedentemente eliminate possono riaffiorare. Tratta qualsiasi dato recuperato come sospetto e verificalo rispetto a una fonte attendibile.

Il mio database viene caricato per essere recuperato?

No. Il file viene letto dal tuo disco e ricostruito nella scheda del tuo browser; lo scanner delle pagine è puro TypeScript e lo scrittore è sqlite3.wasm servito dalla stessa origine. Puoi osservare la scheda Rete e confermare che 0 byte escono — cosa che conta per cronologie di chat, gestori di password, backup e dati sanitari.

Correlati: Ripara un database SQLite · Ripara una cartella di lavoro Excel · "The file is damaged and could not be repaired" · Verifica che non viene caricato nulla