"""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("", 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