synology_apm_repo.sdk.storage.sqlite module¶
SQLite access helper — a free function, not an ObjectStore Protocol
method, since WAL handling is shared logic every backend needs
identically.
Two paths:
Fast path: no non-zero
-walsidecar present -> open directly via a read-only, immutablefile:URI. Only valid forLocalFsStore— the only place in the SDK that assumes a store’s paths map onto real filesystem paths, which is why this is a free function rather than a method every backend must implement; S3/Azure stores always take the slow path.Slow path: a non-zero
-walsidecar exists (real observed case:copy_meta_file/target.db) -> copy the main file plus any present-wal/-shmsidecars into a temp directory and open that copy read-write (letting SQLite’s own recovery replay the WAL), so the original store’s bytes are never touched. Only-wal’s size gates this decision — a non-zero-shmalone does not trigger it.
The read-only/read-write split between the two is the whole reason
apply_index_hint lives here rather than in storage/table.py: only
a connection onto a copy we own can be given an index, and this module is
where that distinction is made.
- async synology_apm_repo.sdk.storage.sqlite.open_sqlite(store, path, *, tmp_dir=None, transform=None)¶
Open an
aiosqliteconnection topathwithinstore— read-only on the fast path, read-write on the slow path’s own private materialized copy. The slow path is taken whenevertransformis given,storeisn’t aLocalFsStore, or a non-zero-walsidecar is present; the fast path is theLocalFsStore-only case with none of those.Returns
(connection, materialized_tmp_dir).materialized_tmp_dirisNoneon the fast path (nothing extra was created); on the slow path it is thetempfile.TemporaryDirectorybacking the materialized copy — the caller must keep a reference to it for as long asconnectionis used, and should call.cleanup()(or simply let it go out of scope) once done.transform, when given, is applied independently to each file’s raw bytes (main file, and any-wal/-shmsidecar) before materializing, and forces the slow path regardless of WAL state.aiosqlitepins each connection to one dedicated thread for its lifetime, funnelling every statement — includingclose()— through it, socheck_same_threadis unnecessary.
- async synology_apm_repo.sdk.storage.sqlite.apply_index_hint(conn, table, columns)¶
A pure declaration of intent — “queries against
tablewill filter/sort bycolumns” — never a command this is guaranteed to fulfil; callers never need to check whether it actually did anything. Safe to call even whencolumnsis already known to be indexed (e.g. a schema-defined index or a PRIMARY KEY): the leading-prefix check below makes that case a cheap no-op, so callers apply this uniformly rather than reasoning per call site about whether it’s needed.Checks whether an existing index already covers
columnsas a leading prefix; skips if so. Otherwise attemptsCREATE INDEX IF NOT EXISTSand lets a genuinely read-only connection’s own answer settle whether that’s even possible: SQLite raisesOperationalError: attempt to write a readonly databasesynchronously and safely against amode=roconnection, caught here and treated as “the hint quietly did nothing” — the caller’s own query afterward just costs a full scan. Any other error still propagates.