"""Small, portable SQLite bind batches for image-index lookups. Older SQLite builds (including some bundled Python distributions) allow only 999 bound parameters per statement. Keep every variable-length IN query below that limit, even when a gallery holds tens of thousands of images. """ from __future__ import annotations from collections.abc import Iterator, Sequence import sqlite3 from typing import TypeVar T = TypeVar("T") SQLITE_BIND_BATCH_SIZE = 900 def iter_sqlite_batches(values: Sequence[T], batch_size: int = SQLITE_BIND_BATCH_SIZE) -> Iterator[Sequence[T]]: if batch_size < 1 or batch_size > SQLITE_BIND_BATCH_SIZE: raise ValueError(f"batch_size must be between 1 and {SQLITE_BIND_BATCH_SIZE}") for start in range(0, len(values), batch_size): yield values[start:start + batch_size] class ClosingConnection(sqlite3.Connection): """sqlite3's default context commits but leaves the handle open on Windows.""" def __exit__(self, *args): try: return super().__exit__(*args) finally: self.close() class BorrowedReadConnection: """The request owns this read-only handle; nested helpers must not close it.""" def __init__(self, connection): self.connection = connection def __enter__(self): return self.connection def __exit__(self, *args): return False