""" QThread-based async worker for all database operations. Emits results/errors via Qt signals so the UI stays responsive. """ import time from PyQt6.QtCore import QThread, pyqtSignal from app.utils.logger import get_logger _log = get_logger(__name__) class QueryWorker(QThread): """Execute a SQL query in a background thread.""" finished = pyqtSignal(list, list, int, float) # cols, rows, rowcount, elapsed_sec error = pyqtSignal(str) script_done = pyqtSignal(list) # list of (cols, rows, cnt, msg) def __init__(self, driver, sql: str, is_script: bool = False, parent=None): super().__init__(parent) self._driver = driver self._sql = sql self._script = is_script def run(self): t0 = time.perf_counter() try: if self._script: results = self._driver.execute_script(self._sql) self.script_done.emit(results) else: cols, rows, cnt = self._driver.execute_query(self._sql) elapsed = time.perf_counter() - t0 _log.debug("Query OK (%.3f s, %d row(s)) SQL: %.200s", elapsed, cnt, self._sql.replace("\n", " ")) self.finished.emit(cols, list(rows), cnt, elapsed) except Exception as e: _log.error("Query failed SQL: %.300s", self._sql.replace("\n", " "), exc_info=True) self.error.emit(str(e)) class SchemaWorker(QThread): """Generic async loader for schema introspection calls.""" result = pyqtSignal(object) error = pyqtSignal(str) def __init__(self, fn, *args, parent=None): super().__init__(parent) self._fn = fn self._args = args def run(self): try: data = self._fn(*self._args) self.result.emit(data) except Exception as e: _log.error("SchemaWorker error fn=%s args=%s", getattr(self._fn, "__name__", "?"), self._args, exc_info=True) self.error.emit(str(e)) class TableDataWorker(QThread): """Load paginated table data in background.""" finished = pyqtSignal(list, list, int) # cols, rows, total_count error = pyqtSignal(str) def __init__(self, driver, database, table, where="", order_by="", limit=1000, offset=0, parent=None): super().__init__(parent) self._driver = driver self._database = database self._table = table self._where = where self._order_by = order_by self._limit = limit self._offset = offset def run(self): try: cols, rows, cnt = self._driver.get_table_data( self._database, self._table, self._where, self._order_by, self._limit, self._offset ) total = self._driver.get_table_row_count( self._database, self._table, self._where ) self.finished.emit(cols, list(rows), total) except Exception as e: _log.error("TableDataWorker error table=%s.%s where=%r", self._database, self._table, self._where, exc_info=True) self.error.emit(str(e))