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
ProduceUn script .sqlUn zip de archivos NDJSON
ParaRecrear la base de datosEntregar los datos a otro programa
Lo leesqlite3, o cualquier herramienta SQLiteCualquier lenguaje con una biblioteca de zip y otra de JSON
Puede ir a una tuberíaSí, cuando se omite --outNo: 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ónObligatoriaDescripció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 SQLiteEn el archivoEn Python
INTEGERUn número JSONint
REALUn número JSON, siempre con punto decimal o exponentefloat
TEXTUna cadena JSONstr
BLOB{"$blob": "<base64>"} dict
NULLnullNone

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" } ]
}
CampoQué le dice
format_versionLa 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, cordeliaQué 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_versionLa versión de esquema de la base de datos de origen, legible sin abrir ningún dato de tabla.
sourceLa 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_schemaContadas 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.
columnsEl 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.
skippedTablas 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.

Véase también