feat(dev_tools): enhance SQL Query Tool — clear, copy, history, edit, hardening
All checks were successful
CI / ruff (push) Successful in 15s
CI / pytest (push) Successful in 2h42m26s
CI / validate (push) Successful in 34s
CI / dependency-scanning (push) Successful in 34s
CI / docs (push) Successful in 54s
CI / deploy (push) Successful in 1m43s

UI: add Clear and Copy-to-clipboard (TSV) buttons, an in-page Recent
Queries pane (localStorage, capped at 20, de-duped) and a pencil-edit
flow for saved queries with a dedicated SQL field in the modal.
Bind Ctrl/Cmd+S to open the save modal (or edit the active saved query).

Backend: harden validate_query with a multi-statement guard that
respects string literals + comments. Stop swallowing record_query_run
errors silently — log via logger.exception so failures show up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 11:24:40 +02:00
parent 64a178f45d
commit e94b6d07bb
5 changed files with 268 additions and 18 deletions

View File

@@ -46,6 +46,12 @@ def _strip_sql_comments(sql: str) -> str:
return result
def _strip_string_literals(sql: str) -> str:
"""Replace single-quoted string literals with a placeholder so their contents
don't trip the multi-statement check."""
return re.sub(r"'(?:''|[^'])*'", "''", sql)
def validate_query(sql: str) -> None:
"""Validate that the SQL query is SELECT-only (no DML/DDL)."""
stripped = sql.strip().rstrip(";")
@@ -60,6 +66,14 @@ def validate_query(sql: str) -> None:
f"Forbidden SQL keyword: {match.group().upper()}. Only SELECT queries are allowed."
)
# Defense-in-depth: reject queries that chain a second statement after `;`.
# SQLAlchemy's `text()` over psycopg generally sends a single statement, but
# this guards against future driver changes and makes intent explicit.
if ";" in _strip_string_literals(code_only):
raise QueryValidationError(
"Multiple statements are not allowed. Submit one SELECT at a time."
)
def _make_json_safe(value: Any) -> Any:
"""Convert a database value to a JSON-serializable representation."""