synology_apm_repo.sdk.storage.sqlite_source module

peel() + SqliteSource — unify the six envelope->SQLite paths this project has:

source

envelope chain

repository db/<name>

raw

copy_meta_file/*/target.db

aHlT? -> raw

version.db.zst

aHlT? -> zstd

saas/*/db/saas_{version,snapshot}

raw

embedded ObjectDB (saas_obj slice)

raw

service-level DB (saas_obj slice)

zstd

Only two envelope kinds exist (aHlT AES-CTR, then optionally a standard ZSTD frame) and each is auto-detected by its own magic bytes, so one small function handles every source in the table above — callers never need to know in advance which of the six they have.

synology_apm_repo.sdk.storage.sqlite_source.is_zstd_frame(head)

Whether head opens with a standard ZSTD frame’s magic number — only the first 4 bytes are ever inspected, so head need not be the full payload. The exact check peel uses internally to decide whether to attempt decompression at all; exposed here so a caller that only wants to know whether something is zstd-framed before deciding how much to even read (e.g. units/saas/services.py’s inspect_object, choosing between a small head read and a full one) doesn’t need its own copy of the Codec Layer’s magic constant — see ARCHITECTURE.md’s “Cross-cutting shared mechanisms” on why drifting from peel()’s own convention is treated as a real bug, not a style nit.

class synology_apm_repo.sdk.storage.sqlite_source.Envelope(value)

Bases: Enum

Which wrapper(s) peel stripped, outermost first — purely informational (diagnostics/--verbose), callers never need to branch on it themselves.

AHLT = 'aHlT'
ZSTD = 'zstd'
synology_apm_repo.sdk.storage.sqlite_source.peel(data, *, vault_key=None, max_zstd_output_size=None)

Strip whichever envelope(s) data actually has, auto-detected by magic, returning (payload, envelopes_stripped).

Raises KeyRequiredError if the data is aHlT-enveloped but no vault_key was given. Never raises for the zstd step failing to apply — a payload simply isn’t zstd-framed if its first 4 bytes don’t match, a perfectly valid outcome (e.g. a plain db/<name> file).

max_zstd_output_size, forwarded to decompress_zstd_stream, bounds how much a genuinely zstd-matching frame may decompress to before raising — for callers peeling content of an unconfirmed type; omit it (default) for a source already known to be a trusted db snapshot.

class synology_apm_repo.sdk.storage.sqlite_source.SqliteSource

Bases: object

Materialize raw SQLite bytes to a private temp file and open a connection to it — the common tail end of all six paths in this module’s docstring, once peel has produced plain SQLite bytes.

Constructed via await SqliteSource.from_bytes(...) / await SqliteSource.from_raw_store(...), async classmethod factories — see Table.create.

Async-context-manager friendly (async with); also safe to just await close directly once done. The temp file is created with a random name in the platform temp dir and unlinked on close — never touches the source repository (read-only invariant). The connection onto that temp file is deliberately writable; see from_bytes.

connection: Connection
async classmethod from_bytes(data)

Materialize plain (post-peel) SQLite data to a private temp file and open a writable connection to it.

Writable because the file is this instance’s own scratch copy, unlinked again by close() — the store’s bytes are already behind us by the time data exists, so the read-only invariant is upheld by what this never opens, not by the mode of this connection. It is also what lets apply_index_hint build a real index here instead of leaving every hinted query a full table scan.

async classmethod from_raw_store(store, path)

Read and open path from store, for a caller that already knows path is never enveloped (db/<name>, saas/*/db/saas_{version,snapshot}). Delegates to open_sqlite, which materializes path itself and also handles a real non-empty -wal sidecar.

async classmethod from_enveloped_store(store, path, *, vault_key)

Read, peel(), and open path from store — for a source that may be aHlT-enveloped (copy_meta_file/*/target.db, FORMAT-SPEC.md: copy_meta_file-layout) and may have a real -wal/-shm sidecar, each peeled independently.

async close()

Safe to call more than once — a second call is a no-op rather than raising FileNotFoundError on the already-unlinked temp file. A caller that closes a provider itself, ahead of the session-wide cleanup that would otherwise close it again at session end, must not crash that later, redundant close.