Skip to content

Commit fa34527

Browse files
committed
Fix CancellableQuery for adapters without cursor support
- Use adapter.execute_query/execute_non_query instead of direct cursor access - Add has_cursor check in commands.py for streaming CSV/JSON output - Fixes "'ClientSync' object has no attribute 'cursor'" error with Turso
1 parent afe101e commit fa34527

2 files changed

Lines changed: 16 additions & 26 deletions

File tree

sqlit/commands.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,12 @@ def cmd_query(
360360
# For unlimited streaming output (CSV/JSON only), use direct cursor access
361361
from .services.query import is_select_query
362362

363-
if max_rows is None and args.format in ("csv", "json") and is_select_query(query):
363+
# Check if connection supports cursors (some adapters like Turso don't)
364+
has_cursor = hasattr(session.connection, "cursor") and callable(
365+
getattr(session.connection, "cursor", None)
366+
)
367+
368+
if max_rows is None and args.format in ("csv", "json") and is_select_query(query) and has_cursor:
364369
# Stream directly from cursor for unlimited CSV/JSON
365370
cursor = session.connection.cursor()
366371
cursor.execute(query)

sqlit/services/cancellable.py

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -100,37 +100,22 @@ def execute(
100100
raise RuntimeError("Query was cancelled")
101101
self._connection = self.adapter.connect(connect_config)
102102

103-
# Execute query
104-
cursor = self._connection.cursor()
105-
cursor.execute(self.sql)
106-
107-
# Check if it's a SELECT query
108-
if is_select_query(self.sql) and cursor.description:
109-
columns = [col[0] for col in cursor.description]
110-
111-
if max_rows:
112-
rows = cursor.fetchmany(max_rows + 1)
113-
truncated = len(rows) > max_rows
114-
if truncated:
115-
rows = rows[:max_rows]
116-
else:
117-
rows = cursor.fetchall()
118-
truncated = False
119-
120-
row_list = [tuple(row) for row in rows]
103+
# Execute query using adapter methods
104+
if is_select_query(self.sql):
105+
columns, rows, truncated = self.adapter.execute_query(
106+
self._connection, self.sql, max_rows
107+
)
121108
return QueryResult(
122109
columns=columns,
123-
rows=row_list,
124-
row_count=len(row_list),
110+
rows=rows,
111+
row_count=len(rows),
125112
truncated=truncated,
126113
)
127114
else:
128115
# Non-SELECT query
129-
rows_affected = cursor.rowcount if cursor.rowcount >= 0 else 0
130-
try:
131-
self._connection.commit()
132-
except Exception:
133-
pass
116+
rows_affected = self.adapter.execute_non_query(
117+
self._connection, self.sql
118+
)
134119
return NonQueryResult(rows_affected=rows_affected)
135120

136121
finally:

0 commit comments

Comments
 (0)