Spaces:
Sleeping
Sleeping
| """ | |
| execute_sql tool β run a SQL query against the task's SQLite database. | |
| Diagnostic (and repair) tool with pass_rate=0.0: the agent can use this to | |
| inspect poisoned rows, update statuses, and clean up corrupted state. | |
| Dangerous keywords (DROP, ATTACH, DETACH) are blocked to prevent accidental | |
| destruction of the benchmark environment. | |
| """ | |
| import logging | |
| import re | |
| import sqlite3 | |
| from pathlib import Path | |
| from debug_env.server.schemas import ToolResult | |
| logger = logging.getLogger(__name__) | |
| _BLOCKED_KEYWORDS = frozenset({"DROP", "ATTACH", "DETACH"}) | |
| _BLOCKED_RE = re.compile( | |
| r"\b(" + "|".join(_BLOCKED_KEYWORDS) + r")\b", re.IGNORECASE | |
| ) | |
| def execute_sql(workdir: str, query: str) -> ToolResult: | |
| """ | |
| Execute *query* against ``{workdir}/app.db`` and return the results. | |
| SELECT queries display rows as a plain-text table. DML statements | |
| (INSERT/UPDATE/DELETE) display the affected row count. | |
| Args: | |
| workdir: Absolute path to the task working directory. | |
| query: SQL query to execute. | |
| Returns: | |
| :class:`ToolResult` with ``pass_rate=0.0`` and query output in ``logs``. | |
| """ | |
| if not query or not query.strip(): | |
| return ToolResult(pass_rate=0.0, logs="No query provided.", success=False) | |
| # Block dangerous statements | |
| match = _BLOCKED_RE.search(query) | |
| if match: | |
| keyword = match.group(0).upper() | |
| return ToolResult( | |
| pass_rate=0.0, | |
| logs=f"SQL keyword '{keyword}' is not permitted.", | |
| success=False, | |
| ) | |
| db_path = Path(workdir) / "app.db" | |
| if not db_path.exists(): | |
| return ToolResult( | |
| pass_rate=0.0, | |
| logs="No app.db found in the workdir. This task may not use a database.", | |
| success=False, | |
| ) | |
| conn = sqlite3.connect(str(db_path)) | |
| conn.row_factory = sqlite3.Row | |
| try: | |
| cursor = conn.execute(query) | |
| if cursor.description: | |
| rows = cursor.fetchall() | |
| if not rows: | |
| result = "(no rows returned)" | |
| else: | |
| col_names = [d[0] for d in cursor.description] | |
| # Compute column widths | |
| widths = [len(c) for c in col_names] | |
| str_rows = [] | |
| for row in rows: | |
| str_row = [str(v) if v is not None else "NULL" for v in row] | |
| str_rows.append(str_row) | |
| for i, cell in enumerate(str_row): | |
| widths[i] = max(widths[i], len(cell)) | |
| def fmt_row(cells: list) -> str: | |
| return " | ".join(cell.ljust(widths[i]) for i, cell in enumerate(cells)) | |
| header = fmt_row(col_names) | |
| divider = "-+-".join("-" * w for w in widths) | |
| body = "\n".join(fmt_row(r) for r in str_rows) | |
| result = f"{header}\n{divider}\n{body}\n({len(rows)} row(s))" | |
| else: | |
| conn.commit() | |
| result = f"OK β {cursor.rowcount} row(s) affected." | |
| except sqlite3.Error as exc: | |
| return ToolResult(pass_rate=0.0, logs=f"SQL error: {exc}", success=False) | |
| finally: | |
| conn.close() | |
| logger.info("execute_sql: query=%r β %d chars output", query[:80], len(result)) | |
| return ToolResult(pass_rate=0.0, logs=result, success=True) | |