synology_apm_repo.sdk.units.saas.objectdb module

Embedded ObjectDB: object_id -> (offset, length) inside a saas_obj. Unlike every other SQLite this project opens, an ObjectDB isn’t addressed by a file_map path of its own — it’s a tiny SQLite file embedded directly in the stream’s dedup content.

Located exclusively via an ``object_db_id`` string ("<streamUuid>_<offset>_<length>") — a direct DedupFile.view(offset, length), no scanning of any kind. That string’s original source is the connector package’s own SnapshotDB (/<volume>/@ActiveBackup-GSuite/db/snapshot.sqlite and the M365 equivalent), which lives outside @ActiveProtectVault/ @ActiveProtectData and is never exported with a vault — but the connector also copies it into the vault’s own object-name index, at backup-completion time, as part of copy_target_version’s additional_meta (see object_name_index.py). That copy is how every application-layer provider — including RawObjectProvider — gets this string automatically, offline, with no scanning at all; a caller supplying one by hand (the CLI’s --object-db-id escape hatch) is the manual exception, not the normal path.

synology_apm_repo.sdk.units.saas.objectdb.parse_object_db_id(object_db_id)

Parse an object_db_id string ("<streamUuid>_<offset>_<length>") into (stream_uuid, offset, length) — the manual escape hatch for a caller that already knows the exact location and wants to hand it in directly rather than going through the object-name index.

Raises:

NotFoundError – object_db_id isn’t shaped like <text>_<int>_<int> — the same category used elsewhere for an unresolvable location.

synology_apm_repo.sdk.units.saas.objectdb.name_object_id_pairs(items)

Filters items down to the (name, object_id) pairs it actually holds, silently dropping anything that doesn’t match — items is expected to be a JSON array of {"name": str, "object_id": str} objects, the shape an object-name index’s own db_objects array uses (object_name_index.py and services.py’s own INDEX-object sniffing both need this identical filter). Returns [] outright when items isn’t even a list.

class synology_apm_repo.sdk.units.saas.objectdb.ObjectDb

Bases: object

object_id -> (offset, length) lookup backed by one embedded ObjectDB slice, materialized via SqliteSource. The envelope chain here is always raw — peel is still run for uniformity, but is a no-op passthrough on real data.

async classmethod from_bytes(data)

Materializing the slice as SQLite and introspecting object_table are both I/O, so construction is an async classmethod factory rather than __init__.

async classmethod load(dedup_file, offset, length)
async close()
async get(object_id)
async object_map()

The full object_id -> (offset, length) mapping.

async synology_apm_repo.sdk.units.saas.objectdb.read_object(object_db, dedup_file, object_id, *, expected_size=None)

Read one object’s bytes eagerly via object_db’s object_id -> (offset, length) lookup — the shared shape behind every “read one already-located object, then parse/transform it” call site (calendar.py, contact.py, site.py’s META lookup, mail.py’s fragment/META reads). Deliberately eager (dedup_file.read(), not .view()): every one of those callers needs the bytes materialized in-process to parse JSON or decompress, unlike drive.py/site.py’s content branch/ raw_object.py, which hand back the final, unmodified content as a lazy dedup_file.view() instead — those call sites must NOT be routed through this function, since that would force an eager read of what’s meant to stay a lazy, possibly-large stream.

expected_size, when given, raises DataCorruptError if the read doesn’t match it — the one extra check some callers (e.g. a mail fragment’s own META-declared size) need that a plain metadata read doesn’t; omit it (the default) to skip the check entirely.