Archiviare un database con archive
Traduzione automatica provvisoria, non revisionata. In caso di dubbio, consultare la versione inglese, che fa fede.
kent archive scrive un database in un archivio di interscambio a file unico:
uno zip che contiene un file NDJSON per ogni tabella con
dati, un manifesto che li descrive e lo schema di origine.
È di sola lettura. kent archive apre il database nella modalità di sola
lettura di SQLite e non vi scrive nulla.
Quale volete, archive o sqldump
Kent ha due vie d'uscita da un database, e servono a lavori diversi. Scegliere quella sbagliata vi costa un passaggio di conversione più avanti.
sqldump |
archive | |
|---|---|---|
| Produce | Uno script .sql | Uno zip di file NDJSON |
| Serve a | Ricreare il database | Consegnare i dati a un altro programma |
| Riletto da | sqlite3, o qualsiasi strumento
SQLite | Qualsiasi linguaggio con una libreria zip e una JSON |
| Può andare in una pipe | Sì, quando --out è
omesso | No: uno zip non può essere scritto sullo standard output |
Se volete i vostri dati di nuovo in SQLite, usate sqldump. Se volete leggerli in
Python, usate archive.
Sintassi
kent archive --in <dbfilename> --out <archive>
Argomenti
| Opzione | Obbligatoria | Descrizione |
|---|---|---|
--in <dbfilename> | Sì | Percorso di un database
.sqlite esistente da leggere. |
--out <archive> | Sì | Dove scrivere l'archivio. |
Entrambe sono obbligatorie. A differenza di sqldump, non
esiste una forma su standard output a cui ripiegare, perché uno zip non può essere scritto in
una pipe.
$ kent archive --in mydata.sqlite
missing required flag: --out
usage: kent archive --in <dbfilename> --out <archive>
Una nota sul nome del file
.kent è l'estensione di questi archivi. Indica un formato definito, il
manifesto, lo schema e la disposizione per tabella descritti sotto, e non una cartella
qualsiasi il cui contenuto sia vostro da riorganizzare.
Kent non la impone, e questo è deliberato. --out accetta il percorso che gli
date, nessun suffisso viene controllato o aggiunto, e il risultato è un normale file zip che
qualunque strumento zip apre, comunque si chiami. Chiamarne uno mydata.zip vi dà
un archivio perfettamente valido: semplicemente non dice cos'è.
Cosa contiene l'archivio
mydata.kent
mimetype always first, uncompressed: application/vnd.venhola.kent
manifest.json what this archive is, and what is in it
schema.sql the source schema, in the same form kent sqldump --schema-only emits
tables/00000001.jsonl one JSON object per line, one line per row
tables/00000002.jsonl the manifest says which table each number holds
...
I file delle tabelle sono numerati anziché prendere il nome dalla propria tabella. Il
nome di una tabella può contenere caratteri che un nome di file non ammette, come virgolette,
due punti o una barra, quindi Kent scrive tables/00000001.jsonl,
tables/00000002.jsonl e così via, nell’ordine in cui le tabelle sono state
scritte. Il numero non dice quale tabella contiene: è il campo tables[].file del
manifest a collegare i due, ed è per questo che l’esempio seguente legge prima il
manifest.
La voce mimetype è ciò che rende riconoscibile un .kent. Ogni
altra voce è compressa, quindi senza di essa nulla identifica il file finché qualcosa non ne
decomprime una parte. Quei 28 byte sono memorizzati non compressi proprio all'inizio, dove
uno strumento può leggerli direttamente dal file: lo stesso trucco di EPUB, per lo stesso
motivo. Potete ignorarla quando leggete un archivio da soli; kent restore la
controlla e rifiuta un archivio che non la porti.
Una tabella senza righe non produce alcun file. Viene invece elencata nell'array
skipped del manifesto con un motivo, così un programma che legge l'archivio può
distinguere una tabella vuota da una lasciata fuori o che lo schema di origine non ha mai
avuto. Uno schema Cordelia ha 129 tabelle e un database reale ne popola circa 15, quindi gran
parte di ciò che il manifesto descrive è ciò che non c'è.
Leggerlo
La libreria standard di Python basta: nessun pacchetto di terze parti e nessun bisogno di avere Kent o Cordelia installati sulla macchina che legge:
import zipfile, json
with zipfile.ZipFile('mydata.kent') as z:
manifest = json.loads(z.read('manifest.json'))
files = {t['name']: t['file'] for t in manifest['tables']}
with z.open(files['Session']) as f:
for line in f:
row = json.loads(line)
print(row['start_time'], row['sport_id'])
Due cose di quel ciclo sono deliberate. Legge una riga alla volta anziché caricare una tabella in memoria, perché una singola tabella può arrivare a decine di migliaia di righe. E apre solo la tabella che le serve: il resto dell'archivio non viene mai decompresso, ed è per questo che il formato è uno zip di file separati e non un unico grande documento JSON.
Le righe sono oggetti JSON anziché array, quindi a una colonna si arriva per nome. Questo
ripete ogni nome di colonna su ogni riga, il che in pratica non costa nulla: la ripetizione è
esattamente ciò che la compressione zip elimina, e row['sport_id'] è molto più
facile da azzeccare di row[47].
Come passano i valori
Le classi di memorizzazione di SQLite sopravvivono al viaggio:
| In SQLite | Nell'archivio | In Python |
|---|---|---|
INTEGER | Un numero JSON | int |
REAL | Un numero JSON, sempre con un punto decimale o un esponente | float |
TEXT | Una stringa JSON | str |
BLOB | {"$blob": "<base64>"} |
dict |
NULL | null | None |
Un REAL di valore intero conserva il punto decimale per non essere riletto
come intero. Un blob viene racchiuso in un oggetto anziché scritto come stringa nuda, perché
il testo base64 è indistinguibile da un testo ordinario che per caso somigli a base64.
Un valore non sopravvive. Not-a-number e infinito vengono scritti come
0.0, e uno zero sostituito non si distingue da uno misurato.
sqldump scrive not-a-number come NULL.
Il manifesto
Un solo file risponde a ogni domanda strutturale sull'archivio, così un programma lo legge una volta e poi sa cosa ha per le mani:
{
"format_version": 1,
"kent": "1.3.4",
"cordelia": "1.3.3",
"exported": "2026-08-29T14:02:11Z",
"schema": { "user_version": 2 },
"source": { "path": "mydata.sqlite", "size_bytes": 5492736,
"modified": "2026-08-29T00:08:26Z" },
"counts": { "tables_in_schema": 129, "tables_exported": 15,
"tables_skipped": 114, "total_rows": 53673 },
"tables": [
{ "name": "Session", "rows": 26, "file": "tables/00000001.jsonl",
"columns": [ { "name": "sport_id", "type": "INTEGER" } ] }
],
"skipped": [ { "name": "AntRx", "reason": "empty" } ]
}
| Campo | Cosa vi dice |
|---|---|
format_version | La versione della disposizione dell'archivio stesso. Un programma che non riconosce il numero dovrebbe rifiutare l'archivio anziché tirare a indovinare. |
kent, cordelia | Quali versioni hanno prodotto l'archivio. Quella di Cordelia è ciò che identifica lo schema: si veda Esportare un database con sqldump per il motivo. |
schema.user_version | La versione di schema del database di origine, leggibile senza aprire alcun dato di tabella. |
source | Percorso, dimensione e ora di modifica del file da cui proviene. Dimensione e ora insieme rispondono a "è questo un archivio del file che penso?", cosa che il solo percorso non fa. |
counts.tables_in_schema | Contate nel database di origine, non in questa build di Cordelia, così l'archivio di un database più vecchio riporta ciò che quel database aveva davvero. |
columns | Il tipo SQLite dichiarato di ogni colonna, in
ordine. È questa l'autorità su come interpretare un valore: un 1 JSON da
solo non può dirvi se la colonna è un intero o un reale di valore intero. |
skipped | Le tabelle senza file nell'archivio, e perché. Oggi
l'unico motivo è empty. |
I nomi exported e tables_exported descrivono l'operazione
anziché il comando che l'ha eseguita, e corrispondono alla riga -- exported:
nell'intestazione di un kent sqldump. Non sono un residuo
del vecchio nome del comando.
Le tabelle di riferimento viaggiano con esso
L'archivio comprende le tabelle di lookup precaricate di Cordelia,
better_labels_definitions e better_labels_lookup, circa
19.700 righe in tutto, anziché lasciarle fuori come zavorra.
È questo che permette all'archivio di rispondere alle proprie domande. Uno
sport_id pari a 5 non significa nulla da solo; risolverlo in un
nome di sport richiede quelle due tabelle. Senza di esse, uno script che possiede un archivio
avrebbe bisogno di un database Cordelia separato solo per interpretare ciò che ha già. Si
comprimono inoltre molto bene: un testo breve e altamente ripetitivo è vicino al caso
migliore per uno zip.
Un database vecchio viene archiviato, non rifiutato
Come sqldump, e a differenza di
info, details e
files, questo comando non rifiuta un database la cui
versione di schema differisce da quella attesa da questa build. Il manifesto registra la
versione che ha trovato, quindi l'archivio dichiara ciò che l'origine era davvero.
Un'esecuzione fallita non lascia alcun archivio, nemmeno parziale. Un percorso passato a
--in che non esiste viene segnalato come mancante e non viene
creato:
$ kent archive --in nope.sqlite --out out.kent
kent archive: nope.sqlite does not exist
$ echo $?
1
Durante l'esecuzione compare una directory temporanea
Le tabelle vengono scritte in una directory chiamata come l'archivio con
.staging in coda, accanto a dove andrà l'archivio, e zippate da lì anziché
tenute in memoria: una singola tabella può essere molto grande. La directory viene rimossa
quando il comando termina, anche in caso di errore. Se ne vedete una rimasta dopo che Kent è
uscito, vale la pena
segnalarlo.
Significa anche che la destinazione ha bisogno di spazio per l'archivio e per il suo contenuto in preparazione allo stesso tempo.
Rileggere un archivio dentro un database
kent restore ricostruisce un file
.sqlite a partire da un archivio:
$ kent restore --in mydata.kent --out restored.sqlite
Riproduce il database da cui l'archivio proviene anziché aggiornarlo, versione dello schema compresa, quindi l'archivio di un database vecchio si ripristina in un database vecchio. Ogni valore e la sua classe di memorizzazione sopravvivono, tranne non-un-numero e infinito, che l'archivio non poteva trasportare in partenza.
Si veda anche Messaggi di errore e codici di uscita e Esportare un database con sqldump.