99 lines
3.3 KiB
Python
99 lines
3.3 KiB
Python
"""
|
|
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))
|
|
|