database_manager.md 2.9 KB

DatabaseManager Module Overview

DatabaseManager is the low-level persistence layer for Bibliotheca. It wraps a SQLite connection, owns prepared statements, and provides a narrow set of CRUD methods that the higher-level BookList uses to keep the database in sync.

Responsibilities

  • Open / close the SQLite database (sqlite3_open / sqlite3_close).
  • Create the schema on first run (ensure_schema).
  • Expose prepared insert/update/delete/select helpers for Book records.
  • Provide a busy handler so concurrent access backs off politely.
  • Guard SQLite calls with a mutex so callers can safely use the manager across threads.

Structure

DatabaseManager
 ├── std::string db_path_   // path to the .sqlite file
 ├── sqlite3* db_           // raw SQLite handle (owned)
 └── std::mutex mtx_        // serialises public API calls

All statements are prepared lazily by prepare_statements() and reused for each operation.

Schema

ensure_schema() creates a single table named books:

CREATE TABLE IF NOT EXISTS books (
  id TEXT PRIMARY KEY,
  title TEXT,
  author TEXT,
  file_path TEXT,
  cover_path TEXT
);

Additional indices or columns should be added here, keeping in mind that the manager is the canonical place to evolve storage.

Public API

Method Description
ensure_schema() Creates tables if they do not exist.
upsert_book(book) INSERT OR REPLACE using the book’s id.
remove_book(id) Deletes the row; returns false if not present.
get_book(id) Loads a single book (optional result).
load_all_books() Returns a vector of all books.

Internally each method locks mtx_, executes the prepared statement, and translates SQLite rows into Book objects.

Busy handling & thread safety

SQLite is file-based and can momentarily return SQLITE_BUSY if another thread or process holds a lock. The static busy_handler callback retries with an exponential backoff. Because all public methods take the mutex, only one thread issues SQL commands at a time, simplifying concurrency.

Extension points

  • Schema migrations: add ALTER TABLE logic inside ensure_schema() and keep the Book serialization/deserialization in sync.
  • Additional queries: expose specialised getters (e.g., search by author) if higher layers start to need them.
  • Alternative backends: swapping SQLite for another storage engine would mean re-implementing this module while keeping BookList untouched.

Expected usage

BookList owns a single DatabaseManager instance, calls ensure_schema() on startup, and uses the CRUD helpers whenever a book changes. Other modules should not talk to SQLite directly; instead they should route all persistence through this manager to keep logic centralised.