luplo.core.id_resolve ===================== .. py:module:: luplo.core.id_resolve .. autoapi-nested-parse:: 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 :data:`MIN_PREFIX_LENGTH` characters is looked up against ``.id::text LIKE prefix || '%'`` with ``LIMIT 2``. Dashes in the input are ignored so users can paste partially-formatted ids. * Zero matches → :class:`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 → :class:`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 ---------- .. autoapisummary:: luplo.core.id_resolve.MIN_PREFIX_LENGTH Functions --------- .. autoapisummary:: luplo.core.id_resolve.resolve_uuid_prefix luplo.core.id_resolve.build_seed_clause Module Contents --------------- .. py:data:: MIN_PREFIX_LENGTH :value: 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. .. py:function:: 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 :async: Resolve a UUID or hex prefix against ``
.id``. :param conn: Open async connection. :param table: Unquoted table name (e.g. ``"items"``). :param value: Either a full canonical UUID string or a hex prefix. :param project_id: Optional project scope. If supplied, the lookup is constrained to ``project_column = project_id`` so prefixes from other projects do not collide. :param label_column: Column used to label sampled rows in the :class:`AmbiguousIdError` message. Tables without a useful label can pass ``"id"`` to get the id back as the label. :param 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 InvalidIdFormatError: When *value* is neither a full UUID nor a valid hex prefix (after stripping dashes). :raises IdTooShortError: When *value* is a hex prefix shorter than :data:`MIN_PREFIX_LENGTH`. :raises AmbiguousIdError: When the prefix matches more than one row. .. py:function:: 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 :func:`resolve_uuid_prefix`. :raises InvalidIdFormatError: When *value* is not a UUID or hex prefix. :raises IdTooShortError: When the hex prefix is shorter than the minimum.