luplo.core.id_resolve

UUID prefix resolution for human-typed identifiers.

luplo identifies most rows by UUIDv4 primary keys; the CLI displays them as 8-character prefixes (id[:8]) and accepts the same prefixes back on input. This module is the single place where prefix → full UUID resolution happens.

Behaviour:

  • A 36-character canonical UUID is returned as-is (fast path; no DB call).

  • A hex prefix of at least MIN_PREFIX_LENGTH characters is looked up against <table>.id::text LIKE prefix || '%' with LIMIT 2. Dashes in the input are ignored so users can paste partially-formatted ids.

  • Zero matches → NotFoundError is the caller’s job; this function returns None instead so callers keep their existing not-found shapes.

  • One match → the full UUID is returned.

  • Two or more matches → AmbiguousIdError is raised carrying the sampled rows. The caller never has to choose silently.

The module is intentionally generic: it takes a table name, an optional project scope, and a label column used only for ambiguity messages. Each domain module wraps this with its own resolve_*_id helper.

Attributes

MIN_PREFIX_LENGTH

Minimum hex characters required for a prefix lookup.

Functions

resolve_uuid_prefix(→ str | None)

Resolve a UUID or hex prefix against <table>.id.

build_seed_clause(→ psycopg.sql.Composable)

Build a SQL fragment that selects rows matching value by id.

Module Contents

luplo.core.id_resolve.MIN_PREFIX_LENGTH = 8

Minimum hex characters required for a prefix lookup.

8 hex characters = 32 bits, which keeps birthday-paradox collision probability under ~1% for project-scoped tables holding fewer than ~10,000 rows. Below this threshold, requiring more characters is cheaper than relying on collision luck.

async luplo.core.id_resolve.resolve_uuid_prefix(conn: psycopg.AsyncConnection[Any], table: str, value: str, *, project_id: str | None = None, label_column: str = 'title', project_column: str = 'project_id') str | None

Resolve a UUID or hex prefix against <table>.id.

Parameters:
  • conn – Open async connection.

  • table – Unquoted table name (e.g. "items").

  • value – Either a full canonical UUID string or a hex prefix.

  • project_id – Optional project scope. If supplied, the lookup is constrained to project_column = project_id so prefixes from other projects do not collide.

  • label_column – Column used to label sampled rows in the AmbiguousIdError message. Tables without a useful label can pass "id" to get the id back as the label.

  • project_column – Column name to scope by; defaults to "project_id".

Returns:

The full UUID string when exactly one row matches, or None when no row matches.

Raises:
luplo.core.id_resolve.build_seed_clause(value: str, params: dict[str, Any]) psycopg.sql.Composable

Build a SQL fragment that selects rows matching value by id.

Returns a clause suitable for a WHERE position (no leading WHERE). Mutates params in place: adds the binding under the key "seed".

Use this when the caller needs to embed prefix matching inside a larger query (e.g. a recursive CTE that walks a supersede chain forward from any matching seed). For standalone lookups, prefer resolve_uuid_prefix().

Raises: