Skip to content

Commit 7556d23

Browse files
committed
Fix SQL query handling to allow trailing semicolons (bug from copilot fix) and improve error checking
1 parent 1110bd4 commit 7556d23

2 files changed

Lines changed: 18 additions & 6 deletions

File tree

src/dbt_mcp/dbt_admin/run_artifacts/store.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,12 @@ def describe_table(self, table_name: str) -> list[dict[str, str]]:
337337

338338
def query(self, sql: str) -> list[dict[str, Any]]:
339339
"""Execute a read-only SQL query. Results capped at 500 rows."""
340-
sanitized = _strip_sql_comments(sql)
340+
sanitized = _strip_sql_comments(sql).strip()
341+
if sanitized.endswith(";"):
342+
sanitized = sanitized[:-1]
341343
if ";" in sanitized:
342344
raise ArtifactQueryError("Multi-statement queries are not allowed.")
343-
tokens = sanitized.strip().upper().split()
345+
tokens = sanitized.upper().split()
344346
for token in tokens:
345347
if token in READONLY_BLOCKED:
346348
raise ArtifactQueryError(
@@ -371,14 +373,19 @@ def search(
371373

372374
fts_table = f"fts_main_{table_name}"
373375
sql = f"""
374-
SELECT t.*, {fts_table}.match_bm25(t.id, ?) AS fts_score
376+
WITH scored AS (
377+
SELECT id, {fts_table}.match_bm25(id, ?) AS fts_score
378+
FROM "{table_name}"
379+
)
380+
SELECT t.*, s.fts_score
375381
FROM "{table_name}" t
376-
WHERE {fts_table}.match_bm25(t.id, ?) IS NOT NULL
377-
ORDER BY fts_score DESC
382+
JOIN scored s ON t.id = s.id
383+
WHERE s.fts_score IS NOT NULL
384+
ORDER BY s.fts_score DESC
378385
LIMIT ?
379386
"""
380387
try:
381-
result = self.conn.execute(sql, [query_text, query_text, limit])
388+
result = self.conn.execute(sql, [query_text, limit])
382389
columns = [desc[0] for desc in result.description]
383390
rows = result.fetchall()
384391
return [

tests/unit/dbt_admin/run_artifacts/test_store.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@ def test_comment_bypass_blocked(self, store: ArtifactStore) -> None:
141141
with pytest.raises(ArtifactQueryError, match="Multi-statement"):
142142
store.query("SELECT 1;/**/DROP TABLE nodes")
143143

144+
def test_trailing_semicolon_is_allowed(self, store: ArtifactStore) -> None:
145+
store._ensure_tables_created()
146+
result = store.query("SELECT 1 AS n;")
147+
assert result == [{"n": 1}]
148+
144149
def test_invalid_sql_raises(self, store: ArtifactStore) -> None:
145150
with pytest.raises(ArtifactQueryError, match="Query failed"):
146151
store.query("SELECT * FROM table_that_does_not_exist_xyz")

0 commit comments

Comments
 (0)