This document captures a reference view of the SQLite schemas Calibre uses, primarily to inform Delb’s future import/migration planning.
Sources:
- The schema listing in this doc is based on an extracted summary (provided in the project discussion) from Calibre’s repository code and SQL resources.
- Calibre upstream code search: https://github.com/kovidgoyal/calibre/search?q=CREATE+TABLE&type=code
Delb supports importing an existing Calibre library as a one-way migration:
- The Calibre library is mounted at Delb’s
calibre/directory. - Calibre’s database is read from
calibre/metadata.db. - Delb writes its own data to
data/delb.db. - Delb copies Calibre files into its own
library/structure and manages them thereafter.
The import is driven from the admin settings page (Library Management):
- Import from Calibre: one-time import of metadata + file copies into Delb’s
library/.
This is intended to be safe and repeatable.
To prevent duplicate imports, Delb stores the Calibre books.id value alongside the Delb book record:
- Delb
books.calibre_book_id⇄ Calibrebooks.id
Delb does not preserve Calibre’s library UUID; it only uses the Calibre book id to make imports idempotent.
Calibre commonly stores formats as files inside the book folder (for example: *.epub, *.pdf, *.mobi, *.azw3), along with sidecar files such as:
metadata.opfcover.jpg/cover.png
Delb records every discovered format as a row in book_files and stores copied files under its own library:
book_files.formatis the lowercased extension (epub,pdf,mobi,azw3, ...)book_files.relative_pathis stored as a Delb-stylelibrary/...path pointing at the copied Delb-managed file.
Delb prefers discovering formats via the Calibre DB when possible, but will fall back to scanning the on-disk book directory when the DB does not expose filenames in a reliable way across Calibre versions.
Delb sets books.cover_image_path to the generated thumbnail (thumb.webp) in the Delb library and stores a source cover
(cover.<ext>) alongside it.
- This may be incomplete. Calibre evolves schemas over time and applies upgrades based on
PRAGMA user_version. - Many elements are implementation-dependent (views, triggers, indexes, tokenizers, custom functions).
- Calibre supports dynamic custom columns, which can cause additional tables/triggers to exist beyond the “core” tables listed below.
- Calibre may clone/backup databases or create temporary databases for various operations; those generally reuse the same schema patterns.
- Delb MVP upload limitation (known): uploading the same book more than once (same author + title) does not currently deconflict. The upload will overwrite the stored file on disk (since the storage path is deterministic) and insert another row in the database. A future improvement is to add a deconfliction flow (prompt user: replace, keep both, cancel) and/or enforce a uniqueness constraint.
This is the main library database in a Calibre library folder. It stores books, authors, tags, series, formats, covers, identifiers, etc.
idINTEGER PRIMARY KEY AUTOINCREMENTtitleTEXT NOT NULL DEFAULT 'Unknown' COLLATE NOCASEsortTEXT COLLATE NOCASEtimestampTIMESTAMP DEFAULT CURRENT_TIMESTAMPuriTEXTseries_indexINTEGER NOT NULL DEFAULT 1author_sortTEXT COLLATE NOCASE (added later via upgrades)isbnTEXT DEFAULT "" COLLATE NOCASE (added later via upgrades)- Other columns may be added by later upgrades (example:
last_modified) depending on Calibre version.
idINTEGER PRIMARY KEYnameTEXT NOT NULL COLLATE NOCASEsortTEXT COLLATE NOCASE- UNIQUE(
name)
idINTEGER PRIMARY KEYnameTEXT NOT NULL COLLATE NOCASEsortTEXT COLLATE NOCASE- UNIQUE(
name)
idINTEGER PRIMARY KEYnameTEXT NOT NULL COLLATE NOCASE- UNIQUE(
name)
idINTEGER PRIMARY KEYnameTEXT NOT NULL COLLATE NOCASEsortTEXT COLLATE NOCASE- UNIQUE(
name)
idINTEGER PRIMARY KEYratingINTEGER CHECK(rating> -1 ANDrating< 11)- UNIQUE(
rating) - Typically pre-populated with values 0..10.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLauthorINTEGER NOT NULL- UNIQUE(
book,author) - Indexes typically exist on
bookandauthor.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLpublisherINTEGER NOT NULL- UNIQUE(
book)- Note: this design implies one publisher per book at the link level.
- Indexes typically exist on
bookandpublisher.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLtagINTEGER NOT NULL- UNIQUE(
book,tag) - Indexes typically exist on
bookandtag.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLseriesINTEGER NOT NULL- UNIQUE(
book) - Indexes typically exist on
bookandseries.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLratingINTEGER NOT NULL- UNIQUE(
book,rating) - Indexes typically exist on
bookandrating.
Stores the actual format payloads (depending on how Calibre is configured/used).
idINTEGER PRIMARY KEYbookINTEGER NOT NULLformatTEXT NOT NULL COLLATE NOCASEuncompressed_sizeINTEGER NOT NULLdataBLOB NOT NULL- UNIQUE(
book,format) - Index typically exists on
book.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLuncompressed_sizeINTEGER NOT NULLdataBLOB NOT NULL- UNIQUE(
book) - Index typically exists on
book.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLtextTEXT NOT NULL COLLATE NOCASE- UNIQUE(
book) - Index typically exists on
book.
idINTEGER PRIMARY KEYformatTEXT NOT NULL COLLATE NOCASEbookINTEGERdataBLOB NOT NULL- UNIQUE(
format,book) - Indexes typically exist on
formatandbook.
idINTEGER PRIMARY KEYtitleTEXT NOT NULLscriptTEXT NOT NULL- UNIQUE(
title)
Calibre’s “custom columns” can be created by users at runtime. The schema is not fixed.
A common normalized pattern (conceptual) for a custom column is:
- A master values table:
id,value(and optional extra columns for series-like data)- UNIQUE(
value)
- A link table:
id,bookINTEGER NOT NULL,valueINTEGER NOT NULL- optional extra column (e.g.
extraREAL) - UNIQUE(
book,value)
- Plus indexes and triggers.
- Denormalized patterns also exist depending on custom column type/config.
For import planning in Delb: expect that a Calibre metadata.db may contain many extra tables related to custom columns.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLnameTEXT NOT NULLvalTEXT NOT NULL- UNIQUE(
book,name)
idINTEGER PRIMARY KEYbookINTEGER NOT NULL- UNIQUE(
book)
idINTEGER PRIMARY KEYuuidTEXT NOT NULL- UNIQUE(
uuid)
idINTEGER PRIMARY KEYbookINTEGER NOT NULLtypeTEXT NOT NULL DEFAULT "isbn" COLLATE NOCASEvalTEXT NOT NULL COLLATE NOCASE- UNIQUE(
book,type)
idINTEGER PRIMARY KEYlang_codeTEXT NOT NULL COLLATE NOCASE- UNIQUE(
lang_code)
idINTEGER PRIMARY KEYbookINTEGER NOT NULLlanguageINTEGER NOT NULL- Likely UNIQUE(
book,language) plus indexes (exact definitions depend on upgrade scripts).
Calibre commonly provides a meta view that aggregates books plus related data (authors, tags, series, formats, comments, rating, publisher, isbn, etc.). The exact columns can vary across versions, but commonly include:
id,title,authors,publisher,rating,timestamp,size,tags,comments,series,series_index,sort,author_sort,formats,isbn
Calibre maintains a separate database for full-text search indexing of book text.
idINTEGER PRIMARY KEYbookINTEGER NOT NULLformatTEXT NOT NULL COLLATE NOCASEin_progressINTEGER NOT NULL DEFAULT FALSE- UNIQUE(
book,format)
idINTEGER PRIMARY KEYbookINTEGER NOT NULLtimestampREAL NOT NULLformatTEXT NOT NULL COLLATE NOCASEformat_hashTEXT NOT NULL COLLATE NOCASEformat_sizeINTEGER NOT NULL DEFAULT 0searchable_textTEXT NOT NULL DEFAULT ''text_sizeINTEGER NOT NULL DEFAULT 0text_hashTEXT NOT NULL COLLATE NOCASE DEFAULT ''err_msgTEXT DEFAULT ''- UNIQUE(
book,format)
-
fts_db.books_ftsUSINGfts5(...)- Uses
content='books_text'andcontent_rowid='id' - Tokenizer:
calibre remove_diacritics 2(custom tokenizer behavior)
- Uses
-
fts_db.books_fts_stemmedUSINGfts5(...)- Tokenizer:
porter calibre remove_diacritics 2
- Tokenizer:
Triggers are used to keep books_fts* in sync with books_text.
Calibre uses a separate notes database with optional full-text search.
idINTEGER PRIMARY KEY AUTOINCREMENTitemINTEGER NOT NULLcolnameTEXT NOT NULL COLLATE NOCASEdocTEXT NOT NULL DEFAULT ''searchable_textTEXT NOT NULL DEFAULT ''ctimeREAL DEFAULT (unixepoch('subsec'))mtimeREAL DEFAULT (unixepoch('subsec'))- UNIQUE(
item,colname)
hashTEXT NOT NULL PRIMARY KEY ON CONFLICT FAILnameTEXT NOT NULL UNIQUE ON CONFLICT FAIL- Declared
WITHOUT ROWID
idINTEGER PRIMARY KEYnoteINTEGER NOT NULLresourceTEXT NOT NULL- FOREIGN KEY(
note) REFERENCESnotes(id) - FOREIGN KEY(
resource) REFERENCESresources(hash) - UNIQUE(
note,resource)
notes_db.notes_ftsUSINGfts5(...)notes_db.notes_fts_stemmedUSINGfts5(...)
Triggers maintain the FTS indexes and resource link integrity.
These databases support Calibre’s server features, caches, and state tracking.
idINTEGER PRIMARY KEYnameTEXT NOT NULLpwTEXT NOT NULL- Note: Calibre stores an unhashed password for digest auth (as documented in its code).
timestampTEXT DEFAULT CURRENT_TIMESTAMPsession_dataTEXT NOT NULL DEFAULT "{}"restrictionTEXT NOT NULL DEFAULT "{}"readonlyTEXT NOT NULL DEFAULT "n"misc_dataTEXT NOT NULL DEFAULT "{}"- UNIQUE(
name)
idINTEGER PRIMARY KEY AUTOINCREMENTlibrary_idTEXT NOT NULLbookINTEGER NOT NULLformatTEXT NOT NULL COLLATE NOCASEuserTEXT NOT NULLcfiTEXT NOT NULLepochINTEGER NOT NULLpos_fracREAL NOT NULL DEFAULT 0tooltipTEXT NOT NULL- UNIQUE(
user,library_id,book,format) - Index on
user
idINTEGER PRIMARY KEY AUTOINCREMENTdateTIMESTAMP DEFAULT CURRENT_TIMESTAMPatimeTIMESTAMP DEFAULT CURRENT_TIMESTAMPfull_nameTEXT NOT NULL UNIQUEetagTEXT NOT NULLmodule_versionINTEGER NOT NULL DEFAULT 1minimum_calibre_versionTEXT NOT NULL DEFAULT "0,0,0"dataBLOB NOT NULL
Calibre can read device-specific SQLite databases (e.g. Kobo). These are created/owned by the device ecosystem, not by Calibre itself, and therefore are not documented here as “Calibre schema”.
Some Calibre database behavior depends on SQLite extensions:
- Custom aggregates/functions (e.g. concatenation helpers) via a native module (C).
- A custom FTS5 tokenizer used by the FTS databases.
- Extension module registration and build metadata are defined in Calibre upstream.
For Delb import planning, this matters because:
- Import may need to replicate derived fields (e.g.
sortconcat-like behavior) in application logic rather than relying on SQLite extensions. - Full-text search ingestion may require re-indexing rather than attempting to import FTS tables directly.
When you’re ready to implement import:
- Decide whether Delb will import:
- only
metadata.dbcore tables, or - also notes and last-read positions, or
- also reconstruct FTS indexes.
- only
- Handle schema variance by reading:
PRAGMA user_versionPRAGMA table_info(<table>)- presence/absence of upgrade-added tables (
identifiers,languages, etc.)
- Treat Calibre custom columns as a first-class discovery process:
- enumerate tables and match Calibre’s custom-column patterns, rather than hardcoding table names.
(Implementation intentionally deferred; this doc is reference-only.)