Archivar una base de datos con archive
Traducción automática provisional, sin revisar. En caso de duda, consulte la versión en inglés, que es la versión de referencia.
kent archive escribe una base de datos en un archivo de intercambio de un
solo fichero: un zip que contiene un archivo NDJSON por
cada tabla con datos, un manifiesto que los describe y el esquema de origen.
Es de solo lectura. kent archive abre la base de datos en el modo de solo
lectura de SQLite y no escribe nada en ella.
Cuál quiere, archive o sqldump
Kent tiene dos salidas de una base de datos, y son para trabajos distintos. Elegir la equivocada le cuesta un paso de conversión más adelante.
sqldump |
archive | |
|---|---|---|
| Produce | Un script .sql | Un zip de archivos NDJSON |
| Para | Recrear la base de datos | Entregar los datos a otro programa |
| Lo lee | sqlite3, o cualquier herramienta
SQLite | Cualquier lenguaje con una biblioteca de zip y otra de JSON |
| Puede ir a una tubería | Sí, cuando se omite --out | No: un zip no puede escribirse en la salida estándar |
Si quiere sus datos de vuelta en SQLite, use sqldump. Si quiere leerlos en
Python, use archive.
Sinopsis
kent archive --in <dbfilename> --out <archive>
Argumentos
| Opción | Obligatoria | Descripción |
|---|---|---|
--in <dbfilename> | Sí | Ruta de una base de datos
.sqlite existente que leer. |
--out <archive> | Sí | Dónde escribir el archivo. |
Ambas son obligatorias. A diferencia de sqldump, no hay
una forma con salida estándar a la que recurrir, porque un zip no puede escribirse en una
tubería.
$ kent archive --in mydata.sqlite
missing required flag: --out
usage: kent archive --in <dbfilename> --out <archive>
Una nota sobre el nombre del archivo
.kent es la extensión de estos archivos. Nombra un formato definido, el
manifiesto, el esquema y la disposición por tablas que se describen abajo, y no una carpeta
arbitraria cuyo contenido sea suyo para reorganizar.
Kent no lo impone, y eso es deliberado. --out toma la ruta que le dé, no se
comprueba ni se añade ningún sufijo, y el resultado es un fichero zip corriente que cualquier
herramienta de zip abre, se llame como se llame. Llamar a uno mydata.zip le da
un archivo perfectamente válido: simplemente no dice lo que es.
Qué contiene el archivo
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
...
Los archivos de tabla están numerados en lugar de llevar el nombre de su tabla. El
nombre de una tabla puede contener caracteres que un nombre de archivo no admite, como
comillas, dos puntos o una barra, así que Kent escribe tables/00000001.jsonl,
tables/00000002.jsonl y así sucesivamente, en el orden en que se escribieron las
tablas. El número no indica qué tabla contiene: el campo tables[].file del
manifiesto es el que relaciona ambos, y por eso el ejemplo siguiente lee primero el
manifiesto.
La entrada mimetype es lo que hace reconocible un .kent. Todas
las demás entradas están comprimidas, así que sin ella nada identifica el fichero hasta que
algo descomprime una parte. Esos 28 bytes se almacenan sin comprimir justo al principio,
donde una herramienta puede leerlos directamente del fichero: el mismo truco que usa EPUB,
por el mismo motivo. Puede ignorarla al leer un archivo por su cuenta;
kent restore la comprueba y rechaza un archivo que no la lleve.
Una tabla sin filas no produce fichero. Se lista en el array skipped del
manifiesto con un motivo, de modo que un programa que lea el archivo pueda distinguir una
tabla vacía de una que se dejó fuera o que el esquema de origen nunca tuvo. Un esquema
Cordelia tiene 129 tablas y una base de datos real puebla unas 15, así que la mayor parte de
lo que describe el manifiesto es lo que no está ahí.
Leerlo
La biblioteca estándar de Python basta: sin paquetes de terceros y sin necesidad de tener Kent o Cordelia instalados en la máquina que lee:
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'])
Dos cosas de ese bucle son deliberadas. Lee una línea cada vez en lugar de cargar una tabla en memoria, porque una sola tabla puede llegar a decenas de miles de filas. Y abre solo la tabla que quiere: el resto del archivo nunca se descomprime, que es la razón de que el formato sea un zip de ficheros separados y no un único documento JSON grande.
Las filas son objetos JSON y no arrays, así que a una columna se llega por su nombre. Eso
repite cada nombre de columna en cada línea, lo que en la práctica no cuesta nada: la
repetición es exactamente lo que elimina la compresión zip, y row['sport_id'] es
mucho más fácil de acertar que row[47].
Cómo se trasladan los valores
Las clases de almacenamiento de SQLite sobreviven al viaje:
| En SQLite | En el archivo | En Python |
|---|---|---|
INTEGER | Un número JSON | int |
REAL | Un número JSON, siempre con punto decimal o exponente | float |
TEXT | Una cadena JSON | str |
BLOB | {"$blob": "<base64>"} |
dict |
NULL | null | None |
Un REAL de valor entero conserva su punto decimal para que no se relea como
un entero. Un blob se envuelve en un objeto en lugar de escribirse como una cadena suelta
porque el texto base64 es indistinguible de un texto corriente que casualmente parezca
base64.
Un valor no sobrevive. No-es-un-número e infinito se escriben como
0.0, y un cero sustituido no se distingue de uno medido.
sqldump escribe no-es-un-número como NULL.
El manifiesto
Un solo fichero responde a todas las preguntas estructurales sobre el archivo, de modo que un programa lo lee una vez y ya sabe qué tiene entre manos:
{
"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 | Qué le dice |
|---|---|
format_version | La versión de la propia disposición del archivo. Un programa que no reconozca el número debería rechazar el archivo en lugar de adivinar. |
kent, cordelia | Qué versiones produjeron el archivo. La de Cordelia es la que identifica el esquema; véase Exportar una base de datos con sqldump para saber por qué. |
schema.user_version | La versión de esquema de la base de datos de origen, legible sin abrir ningún dato de tabla. |
source | La ruta, el tamaño y la hora de modificación del fichero del que procede. Tamaño y hora juntos responden a «¿es este un archivo del fichero que creo?», cosa que una ruta por sí sola no hace. |
counts.tables_in_schema | Contadas en la base de datos de origen, no en esta compilación de Cordelia, de modo que un archivo de una base antigua informa de lo que esa base tenía realmente. |
columns | El tipo SQLite declarado de cada columna, en orden.
Esta es la autoridad sobre cómo interpretar un valor: un 1 de JSON por sí
solo no puede decirle si la columna es un entero o un real de valor entero. |
skipped | Tablas sin fichero en el archivo, y por qué. Hoy el
único motivo es empty. |
Los nombres exported y tables_exported describen la operación y
no el comando que la ejecutó, y coinciden con la línea -- exported: de una
cabecera de kent sqldump. No son un resto del nombre
anterior del comando.
Las tablas de referencia viajan con él
El archivo incluye las tablas de consulta precargadas de Cordelia
, better_labels_definitions y better_labels_lookup, unas
19 700 filas entre las dos, en lugar de dejarlas fuera por
volumen.
Eso es lo que hace que el archivo responda a sus propias preguntas. Un
sport_id de 5 no significa nada por sí solo; resolverlo a un nombre
de deporte necesita esas dos tablas. Sin ellas, un script que tuviera un archivo necesitaría
una base de datos Cordelia aparte solo para interpretar lo que ya tiene. Además se comprimen
extremadamente bien: un texto corto y muy repetitivo se acerca al mejor caso para un zip.
Una base de datos antigua se archiva, no se rechaza
Como sqldump, y a diferencia de
info, details y
files, este comando no rechaza una base de datos cuya
versión de esquema difiera de la que espera esta compilación. El manifiesto registra la
versión que encontró, así que el archivo declara lo que el origen realmente era.
Una ejecución fallida no deja ningún archivo detrás, ni uno parcial. Una ruta dada a
--in que no existe se informa como ausente y no se crea:
$ kent archive --in nope.sqlite --out out.kent
kent archive: nope.sqlite does not exist
$ echo $?
1
Aparece un directorio temporal mientras se ejecuta
Las tablas se escriben en un directorio con el nombre del archivo y el sufijo
.staging, junto a donde va a ir el archivo, y se comprimen desde ahí en lugar de
mantenerse en memoria: una sola tabla puede ser enorme. El directorio se elimina cuando el
comando termina, también cuando falla. Si ve uno ahí después de que Kent haya salido, eso
merece informarse.
También significa que el destino necesita sitio para el archivo y su contenido preparado al mismo tiempo.
Volver a leer un archivo hacia una base de datos
kent restore reconstruye un fichero
.sqlite a partir de un archivo:
$ kent restore --in mydata.kent --out restored.sqlite
Reproduce la base de datos de la que salió el archivo en lugar de ponerla al día, incluyendo su versión de esquema, así que un archivo de una base de datos antigua se restaura a una base de datos antigua. Todos los valores y su clase de almacenamiento sobreviven, salvo no-es-un-número e infinito, que el archivo no podía llevar de entrada.
Véase también Mensajes de error y códigos de salida y Exportar una base de datos con sqldump.