Zalgorithm

Create and populate a persistent FTS5 table

The SQLite virtual tables that I’ve been working with so far have all been temporary tables. The CREATE VIRTUAL TABLE command doesn’t mean create a temporary table. What’s been making the tables temporary is the temp. prefix on the table name. Here’s how to populate an FTS5 table and persist it to disk:

"""Create and populate a persistent FTS5 table from key_bindings."""

import argparse
import sqlite3
from contextlib import closing
from pathlib import Path


def create_fts(database: Path) -> int:
    """Create the FTS table if needed and refresh its contents atomically."""
    # mode=rw allows writes but requires an existing database file.
    # ruff: ignore[SIM117]  the nesting is intentional here.
    with closing(
        sqlite3.connect(database.resolve().as_uri() + "?mode=rw", uri=True)
    ) as connection:
        with connection:
            # Explicitly begin so table creation is also part of the transaction.
            # The context manager commits on success or rolls back on failure.
            connection.execute("BEGIN")
            # main stores the table in the database file; temp would make it
            # disappear when this connection closes.
            connection.execute("""
                CREATE VIRTUAL TABLE IF NOT EXISTS main.key_bindings_fts USING fts5(
                    key UNINDEXED,
                    description,
                    tokenize = 'porter unicode61'
                )
            """)
            # Refresh the separate searchable copy, including removing entries
            # that no longer exist in the source. Repeated runs won't duplicate rows.
            connection.execute("DELETE FROM main.key_bindings_fts")
            connection.execute("""
                INSERT INTO main.key_bindings_fts (rowid, key, description)
                SELECT id, key, description FROM main.key_bindings
            """)
            count = connection.execute(
                "SELECT COUNT(*) FROM main.key_bindings_fts"
            ).fetchone()[0]
    return count


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "database",
        nargs="?",
        type=Path,
        default=Path("tmux_key_bindings.sqlite3"),
        help="Existing SQLite file (default: tmux_key_bindings.sqlite3)",
    )
    args = parser.parse_args()
    try:
        count = create_fts(args.database)
    except sqlite3.Error as error:
        parser.exit(1, f"FTS setup failed: {error}\n")
    print(
        f"Loaded {count} entries into persistent key_bindings_fts in {args.database}."
    )


if __name__ == "__main__":
    main()