Run schema prefetch work on a dedicated background thread.
| 49 | |
| 50 | |
| 51 | class SchemaPrefetcher: |
| 52 | """Run schema prefetch work on a dedicated background thread.""" |
| 53 | |
| 54 | def __init__(self, mycli: 'MyCli') -> None: |
| 55 | self.mycli = mycli |
| 56 | self._thread: threading.Thread | None = None |
| 57 | self._cancel = threading.Event() |
| 58 | self._loaded: set[str] = set() |
| 59 | |
| 60 | def is_prefetching(self) -> bool: |
| 61 | return bool(self._thread and self._thread.is_alive()) |
| 62 | |
| 63 | def clear_loaded(self) -> None: |
| 64 | """Forget which schemas have been prefetched (used on reset).""" |
| 65 | self._loaded.clear() |
| 66 | |
| 67 | def stop(self, timeout: float = 2.0) -> None: |
| 68 | """Signal the background thread to stop and wait briefly for it.""" |
| 69 | if self._thread and self._thread.is_alive(): |
| 70 | self._cancel.set() |
| 71 | self._thread.join(timeout=timeout) |
| 72 | self._cancel = threading.Event() |
| 73 | self._thread = None |
| 74 | |
| 75 | def start_configured(self) -> None: |
| 76 | """Start prefetching based on the user's prefetch settings.""" |
| 77 | mode = getattr(self.mycli, 'prefetch_schemas_mode', PrefetchMode.ALWAYS.value) |
| 78 | schema_list = getattr(self.mycli, 'prefetch_schemas_list', []) |
| 79 | parsed = parse_prefetch_config(mode, schema_list) |
| 80 | if parsed is not None and not parsed: |
| 81 | # ``never`` or ``listed`` with an empty list — nothing to do. |
| 82 | return |
| 83 | self._start(parsed) |
| 84 | |
| 85 | def prefetch_schema_now(self, schema: str) -> None: |
| 86 | """Fetch *schema* immediately on a background thread. |
| 87 | |
| 88 | Used when a user manually switches to a schema. The method |
| 89 | returns quickly; the actual work happens in the new thread. |
| 90 | """ |
| 91 | if not schema: |
| 92 | return |
| 93 | # Avoid double-fetching while a full-prefetch pass is running. |
| 94 | self.stop() |
| 95 | self._start([schema]) |
| 96 | |
| 97 | def _start(self, schemas: Iterable[str] | None) -> None: |
| 98 | """Spawn the background worker. |
| 99 | |
| 100 | ``schemas=None`` defers resolution to the worker, which lists |
| 101 | every database via its own dedicated connection — the main |
| 102 | thread's ``sqlexecute`` must not be used here since the worker |
| 103 | would race with the REPL. |
| 104 | """ |
| 105 | self.stop() |
| 106 | queue: list[str] | None = None if schemas is None else list(schemas) |
| 107 | self._cancel = threading.Event() |
| 108 | self._thread = threading.Thread( |
no outgoing calls