mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-25 18:40:06 -04:00
The database now lives at <media>/config/wrolpi.db so a library (files, configs, AND database) travels with the drive between WROLPi systems. Core: - wrolpi/db.py: lazy SQLite engine factory (WAL, busy_timeout=30s, foreign_keys=ON); get_db_curs yields a plain sqlite3.Cursor; native sqlite3 paramstyle everywhere (no compatibility shims); global sqlite3 adapters keep raw-SQL datetime formats identical to the ORM's. - wrolpi/fts.py: FTS5 external-content search replacing tsvector/GIN. Injection-proof websearch->FTS5 translator, bm25(10,4,2,1) reproducing A/B/C/D weighting, fast(abc)/deep column filtering, snippet headlines. Stemming contract pinned by dedicated tests (watch/watches/watched/ watching are interchangeable). - wrolpi/schema_ddl.py: SQLite triggers shared verbatim by the alembic baseline and test DBs; fixes the channel video_count DELETE/UPDATE bugs and a latent total_size bug (it used the video id as channel id). - wrolpi/db_bootstrap.py: startup auto-create/migrate; refuses databases written by a newer WROLPi (db_version_mismatch); SQLite 3.40 floor; network-filesystem refusal; never creates/migrates/trusts a database beneath an unmounted production media directory (shadow-DB guard). - Alembic: 85 Postgres-era migrations squashed into one baseline; revision ids are dates (2026_07_10_1350) so filenames self-sort and alembic_version says when a schema was created; render_as_batch=True. - All raw Postgres SQL rewritten (ANY->json_each IN, array_agg @> -> HAVING COUNT(DISTINCT), unnest->executemany/UPDATE..FROM, generate_series histograms->strftime GROUP BY, DISTINCT ON-> row_number(), writable CTEs->two statements, jsonb_*->json_*, explicit NULLS FIRST/LAST, LIMIT-before-OFFSET). Simple statements converted to SQLAlchemy instead (flag upsert, download retry/summary/tag checks, channel-by-tag estimate, Directory delete). - Config imports count skipped entries; background dumps are blocked after a lossy import so partial imports can never overwrite YAML configs (they are the sole source of truth for rebuilds now). - Search tolerates explicit JSON nulls for limit/offset (Postgres treated OFFSET NULL as 0; SQLite errors). - file_group.id/doc_section.id render as INTEGER PRIMARY KEY on SQLite (BIGINT is not a rowid alias; FTS5 content_rowid requires it). Ops teardown: docker-compose db service, CI postgres sidecars, initialize_api_db.sh, controller postgresql service, psycopg2-binary all removed. upgrade.sh stops/disables postgresql (data is NOT deleted; `systemctl enable --now postgresql` restores it); systemd units no longer Want postgresql.service. Existing-user upgrade: first boot creates+migrates the DB, configs import (tags recreate their FileGroups from disk), a refresh rebuilds the library index. QA'd on the 10.0.0.8 RPi: rebuild matched the Postgres baseline exactly; survived an uncontrolled hard reboot with clean integrity checks. Accepted behavior changes: FTS5 has no stopwords; bm25 length normalization shifts some rank orders; fast-path d_headline is un-highlighted; all-negative queries match nothing. Deferred: Debian/trixie package lists (incl. sqlite3 CLI), Cypress re-baseline, VACUUM INTO snapshots, exFAT/NTFS warning, auto-kicked rebuild refresh with progress UX. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
95 lines
2.5 KiB
INI
95 lines
2.5 KiB
INI
# A generic, single database configuration.
|
|
|
|
[alembic]
|
|
# path to migration scripts
|
|
script_location = alembic
|
|
|
|
# template used to generate migration files
|
|
# file_template = %%(rev)s_%%(slug)s
|
|
|
|
# sys.path path, will be prepended to sys.path if present.
|
|
# defaults to the current working directory.
|
|
prepend_sys_path = .
|
|
|
|
# timezone to use when rendering the date
|
|
# within the migration file as well as the filename.
|
|
# string value is passed to dateutil.tz.gettz()
|
|
# leave blank for localtime
|
|
# timezone =
|
|
|
|
# max length of characters to apply to the
|
|
# "slug" field
|
|
# truncate_slug_length = 40
|
|
|
|
# set to 'true' to run the environment during
|
|
# the 'revision' command, regardless of autogenerate
|
|
# revision_environment = false
|
|
|
|
# set to 'true' to allow .pyc and .pyo files without
|
|
# a source .py file to be detected as revisions in the
|
|
# versions/ directory
|
|
# sourceless = false
|
|
|
|
# version location specification; this defaults
|
|
# to alembic/versions. When using multiple version
|
|
# directories, initial revisions must be specified with --version-path
|
|
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
|
|
|
|
# Revision ids ARE dates (see process_revision_directives in env.py), so filenames self-sort
|
|
# chronologically and the alembic_version table says when a database's schema was created.
|
|
file_template = %%(rev)s_%%(slug)s
|
|
|
|
# the output encoding used when revision files
|
|
# are written from script.py.mako
|
|
# output_encoding = utf-8
|
|
|
|
# Placeholder: env.py derives the real URL from the media directory (see wrolpi.db.get_db_uri);
|
|
# `main.py db ...` overrides it explicitly.
|
|
sqlalchemy.url = sqlite:///CHANGEME
|
|
|
|
|
|
[post_write_hooks]
|
|
# post_write_hooks defines scripts or Python functions that are run
|
|
# on newly generated revision scripts. See the documentation for further
|
|
# detail and examples
|
|
|
|
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
|
# hooks = black
|
|
# black.type = console_scripts
|
|
# black.entrypoint = black
|
|
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
|
|
|
# Logging configuration
|
|
[loggers]
|
|
keys = root,sqlalchemy,alembic
|
|
|
|
[handlers]
|
|
keys = console
|
|
|
|
[formatters]
|
|
keys = generic
|
|
|
|
[logger_root]
|
|
level = WARN
|
|
handlers = console
|
|
qualname =
|
|
|
|
[logger_sqlalchemy]
|
|
level = WARN
|
|
handlers =
|
|
qualname = sqlalchemy.engine
|
|
|
|
[logger_alembic]
|
|
level = INFO
|
|
handlers =
|
|
qualname = alembic
|
|
|
|
[handler_console]
|
|
class = StreamHandler
|
|
args = (sys.stderr,)
|
|
level = NOTSET
|
|
formatter = generic
|
|
|
|
[formatter_generic]
|
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
|
datefmt = %H:%M:%S
|