synology_apm_repo.sdk.units.saas.object_name_index module

Resolves a per-version, connector-recorded index from copy_target_version.version_spec, plus the shared lookup helpers (resolve_service_db, read_indexed_table, read_id_to_name_map, read_grouped_names) every SaaS provider’s own secondary-table lookups build on.

The connector records, at backup-completion time, exactly which object holds each named service DB and exactly which physical location (<stream_uuid>_<offset>_<length>, the shape parse_object_db_id parses) holds all of them — a lookup into fixed, connector-written bookkeeping, never a schema-scanning guess: a schema-only scan could never safely replace this anyway, since some tables are byte-for-byte identical in schema across workload variants (Archive Mail’s mail_table vs. regular Mail’s) and only the index’s own naming, not the schema, can tell them apart. version_spec may be vault-key encrypted — see catalog.version.parse_version_spec, the shared decrypt-then-parse entry point for this column. Every function here resolves to None rather than raising when the index simply isn’t recorded; a resolved index whose own content later fails to validate is a distinct case, surfaced by provider.py’s own _open_table_via_index, not here.

class synology_apm_repo.sdk.units.saas.object_name_index.ObjectNameIndex(stream_uuid, offset, length, object_ids)

Bases: object

One version’s connector-recorded map of indexed db name -> object_id (object_ids), plus exactly which ObjectDbCandidate (offset/length) holds all of them — parsed straight from object_db_id, the authoritative physical location; nothing downstream of this ever needs to scan for it.

stream_uuid: str
offset: int
length: int
object_ids: dict[str, str]
async synology_apm_repo.sdk.units.saas.object_name_index.resolve_object_name_index(repo, version)

None on any failure to locate the index — no copy_target_version table, no row for this version, unparseable/undecryptable version_spec, or a missing/malformed additional_meta: all shapes of “no index recorded here” (an old repository, or a connector version predating this bookkeeping), never corruption. Never raises for those; a resolved index whose content later fails to validate is a distinct case, handled by this module’s callers, not here.

async synology_apm_repo.sdk.units.saas.object_name_index.resolve_service_db(dedup_file, object_db, object_name_index, object_names, table_name)

The shared “no-scan” resolution loop behind both SaasWorkloadProvider._open_table_via_index (which keeps the returned SqliteSource open long-term) and read_indexed_table (which reads it once and closes it immediately) — only how each caller disposes of a successful result differs, not how one is found. Tries every object_names alias against object_db (already loaded at object_name_index’s own location) in order, opening and returning the first whose resolved object both exists and actually defines table_name. Returns None, closing nothing, when no alias resolves.

async synology_apm_repo.sdk.units.saas.object_name_index.read_indexed_table(dedup_file, object_name_index, object_names, table_name, reader)

Best-effort secondary-table lookup behind the object-name index (M365’s mail_folder_table/contact_folder_table, GWS’s own label/group name tables — see mail.py/contact.py), as opposed to SaasWorkloadProvider.create’s own primary-table resolution, which this doesn’t replace. Hands reader the live connection resolve_service_db resolves.

None on object_name_index being None, every name missing, or any read/decompress/validate failure — this is enrichment (a nicer display name, an extra attrs field), never required content, so a caller degrades to showing less rather than failing.

By design, no schema-only scan: the same table name can legitimately mean two different real tables depending on which db holds it (see mail.py’s own _gws_mail_labels), so only the index’s own naming can tell them apart.

async synology_apm_repo.sdk.units.saas.object_name_index.read_id_to_name_map(dedup_file, object_name_index, object_names, table_name, *, id_column, name_column)

read_indexed_table specialized for the “id -> display name” shape every folder/label/group definitions table in this project shares (mail_folder_table, contact_folder_table, mail_label_table’s definitions half, group_table, …) — table_name’s own id_column/name_column values, keyed by str(id_column value).

async synology_apm_repo.sdk.units.saas.object_name_index.read_grouped_names(dedup_file, object_name_index, *, definition_names, definition_table, id_column, name_column, membership_names, membership_table, item_column, group_column)

Best-effort item_id -> [real group/label names] map, built from a definitions table (id_column -> name_column) plus a membership table (item_column, group_column) — the shape GWS’s own many-to-many label/group mechanisms share (mail.py’s labels, contact.py’s groups). None if either half is unavailable — a caller degrades to showing no labels/groups at all, never a wrong or incomplete join.