All checks were successful
技能自动化发布 / release (push) Successful in 9s
新增 DATA_SOURCES.md、data-sources 示例与双后端 connection 骨架;CLI 与宿主 UI 可读写同一库。要求匠厂 Host ≥ 6.9.28。 Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
2.6 KiB
Python
97 lines
2.6 KiB
Python
"""统一 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)
|