Arquivar um banco de dados com archive

Tradução automática provisória, não revisada. Em caso de dúvida, consulte a versão em inglês, que é a versão de referência.

O kent archive grava um banco de dados em um arquivo de intercâmbio de um só ficheiro: um zip contendo um arquivo NDJSON por tabela com dados, um manifesto que os descreve e o esquema de origem.

Ele é somente leitura. O kent archive abre o banco de dados no modo somente leitura do SQLite e não grava nada nele.

Qual você quer, archive ou sqldump

O Kent tem duas saídas de um banco de dados, e elas servem a trabalhos diferentes. Escolher a errada custa a você uma etapa de conversão depois.

sqldump archive
ProduzUm script .sqlUm zip de arquivos NDJSON
ParaRecriar o banco de dadosEntregar os dados a outro programa
Lido de volta porsqlite3, ou qualquer ferramenta SQLiteQualquer linguagem com uma biblioteca de zip e uma de JSON
Pode ir para um encadeamentoSim, quando --out é omitidoNão, um zip não pode ser gravado na saída padrão

Se você quer seus dados de volta no SQLite, use o sqldump. Se quer lê-los em Python, use o archive.

Sinopse

kent archive --in <dbfilename> --out <archive>

Argumentos

OpçãoObrigatóriaDescrição
--in <dbfilename>SimCaminho de um banco de dados .sqlite existente a ler.
--out <archive>SimOnde gravar o arquivo.

As duas são obrigatórias. Ao contrário do sqldump, não há uma forma com saída padrão à qual recorrer, porque um zip não pode ser gravado em um encadeamento.

$ kent archive --in mydata.sqlite
missing required flag: --out
usage: kent archive --in <dbfilename> --out <archive>

Uma observação sobre o nome do arquivo

.kent é a extensão desses arquivos. Ela nomeia um formato definido, o manifesto, o esquema e a disposição por tabela descritos abaixo, e não uma pasta arbitrária cujo conteúdo seja seu para rearranjar.

O Kent não a impõe, e isso é deliberado. O --out aceita o caminho que você der, nenhum sufixo é verificado ou acrescentado, e o resultado é um arquivo zip comum que qualquer ferramenta de zip abre, seja qual for o nome. Chamar um de mydata.zip dá a você um arquivo perfeitamente válido, ele apenas não diz o que é.

O que o arquivo contém

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
  ...

Os arquivos de tabela são numerados em vez de receberem o nome da sua tabela. O nome de uma tabela pode conter caracteres que um nome de arquivo não aceita, como aspas, dois-pontos ou barra, então o Kent grava tables/00000001.jsonl, tables/00000002.jsonl e assim por diante, na ordem em que as tabelas foram gravadas. O número não diz qual tabela ele contém: o campo tables[].file do manifesto é o que liga um ao outro, e por isso o exemplo abaixo lê o manifesto primeiro.

A entrada mimetype é o que torna um .kent reconhecível. Todas as outras entradas são comprimidas, então sem ela nada identifica o arquivo até que algo descomprima parte dele. Esses 28 bytes ficam armazenados sem compressão logo no início, onde uma ferramenta pode lê-los diretamente do arquivo, o mesmo truque que o EPUB usa, pelo mesmo motivo. Você pode ignorá-la ao ler um arquivo por conta própria; o kent restore a verifica e recusa um arquivo que não a traga.

Uma tabela sem linhas não produz arquivo. Ela é listada no array skipped do manifesto com um motivo, para que um programa que leia o arquivo consiga distinguir uma tabela vazia de uma que foi deixada de fora ou que o esquema de origem nunca teve. Um esquema Cordelia tem 129 tabelas e um banco de dados real popula cerca de 15, então a maior parte do que o manifesto descreve é o que não está lá.

Lendo-o

A biblioteca padrão do Python basta, nenhum pacote de terceiros, e sem necessidade de ter o Kent ou o Cordelia instalados na máquina que faz a leitura:

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'])

Duas coisas nesse laço são deliberadas. Ele lê uma linha por vez em vez de carregar uma tabela na memória, porque uma única tabela pode chegar a dezenas de milhares de linhas. E abre apenas a tabela que quer, o resto do arquivo nunca é descomprimido, que é a razão de o formato ser um zip de arquivos separados em vez de um grande documento JSON.

As linhas são objetos JSON em vez de arrays, então uma coluna é alcançada pelo nome. Isso repete cada nome de coluna em cada linha, o que na prática não custa nada: a repetição é exatamente o que a compressão zip remove, e row['sport_id'] é bem mais fácil de acertar do que row[47].

Como os valores atravessam

As classes de armazenamento do SQLite sobrevivem à viagem:

No SQLiteNo arquivoEm Python
INTEGERUm número JSONint
REALUm número JSON, sempre com ponto decimal ou expoentefloat
TEXTUma string JSONstr
BLOB{"$blob": "<base64>"} dict
NULLnullNone

Um REAL de valor inteiro mantém seu ponto decimal para não ser relido como um inteiro. Um blob é embrulhado em um objeto em vez de gravado como uma string solta porque texto base64 é indistinguível de texto comum que por acaso pareça base64.

Um valor não sobrevive. Not-a-number e infinito são gravados como 0.0, e um zero substituído não se distingue de um medido. sqldump grava not-a-number como NULL.

O manifesto

Um único arquivo responde a todas as perguntas estruturais sobre o arquivo, então um programa o lê uma vez e depois sabe o que está segurando:

{
  "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" } ]
}
CampoO que ele diz a você
format_versionA versão da própria disposição do arquivo. Um programa que não reconheça o número deve recusar o arquivo em vez de adivinhar.
kent, cordeliaQuais versões produziram o arquivo. A do Cordelia é a que identifica o esquema. Veja Exportar um banco de dados com sqldump para o motivo.
schema.user_versionA versão de esquema do banco de dados de origem, legível sem abrir nenhum dado de tabela.
sourceO caminho, o tamanho e a hora de modificação do arquivo de onde isso veio. Tamanho e hora juntos respondem a "este é um arquivo do ficheiro que eu penso?", o que um caminho sozinho não faz.
counts.tables_in_schemaContadas no banco de dados de origem, não nesta compilação do Cordelia, de modo que um arquivo de um banco mais antigo relata o que aquele banco de fato tinha.
columnsO tipo SQLite declarado de cada coluna, em ordem. Esta é a autoridade sobre como interpretar um valor, um 1 em JSON, sozinho, não pode dizer se a coluna é um inteiro ou um real de valor inteiro.
skippedTabelas sem arquivo no pacote, e por quê. Hoje o único motivo é empty.

Os nomes exported e tables_exported descrevem a operação em vez do comando que a executou, e correspondem à linha -- exported: no cabeçalho de um kent sqldump. Não são um resquício do nome antigo do comando.

As tabelas de referência viajam junto

O arquivo inclui as tabelas de consulta pré-carregadas do Cordelia, better_labels_definitions e better_labels_lookup, cerca de 19.700 linhas entre as duas, em vez de deixá-las de fora como volume.

É isso que faz o arquivo responder às próprias perguntas. Um sport_id igual a 5 não significa nada sozinho; resolvê-lo para um nome de esporte exige essas duas tabelas. Sem elas, um script que tivesse um arquivo precisaria de um banco de dados Cordelia à parte só para interpretar o que já tem. Elas também comprimem extremamente bem, texto curto e muito repetitivo está perto do melhor caso para um zip.

Um banco de dados antigo é arquivado, não recusado

Como o sqldump, e ao contrário de info, details e files, este comando não recusa um banco de dados cuja versão de esquema difere da que esta compilação espera. O manifesto registra a versão que encontrou, então o arquivo declara o que a origem realmente era.

Uma execução que falha não deixa arquivo algum para trás, nem um parcial. Um caminho dado a --in que não existe é relatado como ausente e não é criado:

$ kent archive --in nope.sqlite --out out.kent
kent archive: nope.sqlite does not exist
$ echo $?
1

Um diretório temporário aparece enquanto ele roda

As tabelas são gravadas em um diretório com o nome do arquivo acrescido de .staging, ao lado de onde o arquivo vai ficar, e zipadas de lá em vez de mantidas em memória, uma única tabela pode ser muito grande. O diretório é removido quando o comando termina, inclusive quando falha. Se você vir um sobrando depois que o Kent saiu, vale a pena relatar.

Isso também significa que o destino precisa de espaço para o arquivo e para seu conteúdo em preparação ao mesmo tempo.

Ler um arquivo de volta para um banco de dados

O kent restore reconstrói um ficheiro .sqlite a partir de um arquivo:

$ kent restore --in mydata.kent --out restored.sqlite

Ele reproduz o banco de dados de que o arquivo veio em vez de atualizá-lo, inclusive sua versão de esquema, então um arquivo de um banco antigo restaura para um banco antigo. Todo valor e sua classe de armazenamento sobrevivem, exceto não-um-número e infinito, que o arquivo não podia carregar desde o início.

Veja também Mensagens de erro e códigos de saída e Exportar um banco de dados com sqldump.

Veja também