Files
SQLmem/src/sqlmem/spec.py
T

68 lines
2.5 KiB
Python

"""Declarative table specs for ``CachingEngine(tables=[...])``.
Instead of the lazy "learn columns from queries" mode, an application can declare
each table up front — its columns, indexes, refresh strategy and datetime columns —
so the engine preloads them and rejects anything undeclared (fail-fast) rather than
silently triggering an expensive lazy load. The legacy ``delta=/ttl=/indexes=``
kwargs keep working; ``tables=`` is converted to the same internal config.
"""
from dataclasses import dataclass, field
from .delta import DeltaConfig
from .schedule import ScheduleSpec, parse_schedule
# Friendly alias for the declarative API; ``Delta`` and ``DeltaConfig`` are the
# same type (``change_column`` + ``key_columns``), so either may be used as a
# ``TableSpec.refresh`` strategy.
Delta = DeltaConfig
@dataclass(frozen=True)
class TTL:
"""Time-based refresh strategy: full-reload the table when older than *seconds*."""
seconds: int
@dataclass(frozen=True)
class Schedule:
"""Time-of-day refresh strategy: full-reload the table at fixed times of day.
*times* is one time of day or a list of them (``"HH:MM"``/``"HH:MM:SS"``
strings or :class:`datetime.time`), interpreted in the local timezone.
Validated eagerly so a typo surfaces at construction, not at the first
refresh tick.
"""
times: ScheduleSpec
def __post_init__(self) -> None:
parse_schedule("<Schedule>", self.times) # eager validation; table name unknown yet
@dataclass(frozen=True)
class TableSpec:
"""Declarative specification of one cached table.
*columns* lists the columns to cache; leave it ``None`` to cache the whole
table (``SELECT *`` semantics) and allow any column. When columns are listed,
a query asking for a column outside the list raises
:class:`~sqlmem.exceptions.UndeclaredError`.
*refresh* is a :class:`Delta` (change-column incremental sync), :class:`TTL`
(time-based full reload) or :class:`Schedule` (full reload at fixed times of
day), or ``None`` for a static table loaded once.
*preload=True* loads the table at startup (in the background by default) so the
first query is a cache hit instead of paying a cold load; a copy already fresh
in the persistent cache is skipped.
"""
name: str
columns: list[str] | None = None
indexes: list[str | list[str]] = field(default_factory=list)
refresh: DeltaConfig | TTL | Schedule | None = None
datetime_columns: list[str] = field(default_factory=list)
preload: bool = False