"""统一 SQLite / MySQL 游标:占位符 ? 与 executescript。""" from __future__ import annotations import re from typing import Any from db.backend import DbBackend _PLACEHOLDER_RE = re.compile(r"\?") def adapt_sql(sql: str, backend: DbBackend) -> str: if backend != "mysql": return sql return _PLACEHOLDER_RE.sub("%s", sql) def _split_sql_script(script: str) -> list[str]: parts: list[str] = [] for chunk in script.split(";"): stmt = chunk.strip() if stmt: parts.append(stmt) return parts class AdaptedCursor: backend: DbBackend def __init__(self, cursor: Any, backend: DbBackend) -> None: self._cursor = cursor self.backend = backend def execute(self, sql: str, params: tuple[Any, ...] | list[Any] | None = None) -> Any: adapted = adapt_sql(sql, self.backend) if params is None: return self._cursor.execute(adapted) return self._cursor.execute(adapted, params) def executemany(self, sql: str, seq_of_params: list[tuple[Any, ...]]) -> Any: return self._cursor.executemany(adapt_sql(sql, self.backend), seq_of_params) def fetchone(self) -> Any: return self._cursor.fetchone() def fetchall(self) -> list[Any]: return self._cursor.fetchall() @property def lastrowid(self) -> int: return int(self._cursor.lastrowid) @property def rowcount(self) -> int: return int(self._cursor.rowcount) def executescript(self, script: str) -> None: if self.backend == "sqlite": self._cursor.connection.executescript(script) return for stmt in _split_sql_script(script): self.execute(stmt) class AdaptedConnection: backend: DbBackend def __init__(self, conn: Any, backend: DbBackend) -> None: self._conn = conn self.backend = backend def cursor(self) -> AdaptedCursor: return AdaptedCursor(self._conn.cursor(), self.backend) def commit(self) -> None: self._conn.commit() def close(self) -> None: self._conn.close() def execute(self, sql: str, params: tuple[Any, ...] | list[Any] | None = None) -> Any: cur = self.cursor() return cur.execute(sql, params) def executemany(self, sql: str, seq_of_params: list[tuple[Any, ...]]) -> Any: cur = self.cursor() return cur.executemany(sql, seq_of_params) def executescript(self, script: str) -> None: if self.backend == "sqlite": self._conn.executescript(script) return cur = self.cursor() for stmt in _split_sql_script(script): cur.execute(stmt)