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
ProduceUno script .sqlUno zip di file NDJSON
Serve aRicreare il databaseConsegnare i dati a un altro programma
Riletto dasqlite3, o qualsiasi strumento SQLiteQualsiasi linguaggio con una libreria zip e una JSON
Può andare in una pipeSì, quando --out è omessoNo: 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

OpzioneObbligatoriaDescrizione
--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 SQLiteNell'archivioIn Python
INTEGERUn numero JSONint
REALUn numero JSON, sempre con un punto decimale o un esponentefloat
TEXTUna stringa JSONstr
BLOB{"$blob": "<base64>"} dict
NULLnullNone

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" } ]
}
CampoCosa vi dice
format_versionLa versione della disposizione dell'archivio stesso. Un programma che non riconosce il numero dovrebbe rifiutare l'archivio anziché tirare a indovinare.
kent, cordeliaQuali 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_versionLa versione di schema del database di origine, leggibile senza aprire alcun dato di tabella.
sourcePercorso, 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_schemaContate 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.
columnsIl 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.
skippedLe 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.

Vedi anche