Skip to content

Commit 548edcc

Browse files
kesmit13claude
andcommitted
Fix _iquery DataFrame conversion for non-tuple results_type
_iquery must always return List[Dict[str, Any]], but when the connection uses a non-tuple results_type (polars, pandas, numpy, arrow), the specialized cursor's fetchall() returns a DataFrame/ndarray instead of tuples. The previous code had two bugs: 1. list() on a DataFrame iterates by columns, producing Series objects instead of row dicts. 2. to_dict(orient='records') is pandas-specific and fails on polars. Dispatch on the raw fetchall() result type before converting to dicts: - pandas DataFrame: to_dict(orient='records') - polars DataFrame: to_dicts() - Arrow Table: to_pydict() with column-to-row transposition - numpy ndarray: tolist() with cursor.description column names - tuples/dicts: existing logic preserved Centralize fix_names camelCase conversion as a single post-processing step applied uniformly to all result types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fe1d8ad commit 548edcc

1 file changed

Lines changed: 49 additions & 8 deletions

File tree

singlestoredb/connection.py

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,17 +1167,58 @@ def _iquery(
11671167
if not re.match(r'^\s*(select|show|call|echo)\s+', oper, flags=re.I):
11681168
return []
11691169
raw = cur.fetchall()
1170-
if hasattr(raw, 'to_dict') and callable(raw.to_dict):
1171-
return raw.to_dict(orient='records')
1172-
out = list(raw)
1173-
if not out:
1170+
if raw is None:
11741171
return []
1175-
if isinstance(out[0], (tuple, list)):
1172+
# pandas DataFrame
1173+
if hasattr(raw, 'to_dict') and hasattr(raw, 'columns'):
1174+
out = raw.to_dict(orient='records')
1175+
# polars DataFrame
1176+
elif hasattr(raw, 'to_dicts') and callable(raw.to_dicts):
1177+
out = raw.to_dicts()
1178+
# arrow Table
1179+
elif hasattr(raw, 'to_pydict') and callable(raw.to_pydict):
1180+
d = raw.to_pydict()
1181+
cols = list(d.keys())
1182+
n = len(next(iter(d.values()))) if d else 0
1183+
out = [{c: d[c][i] for c in cols} for i in range(n)]
1184+
# numpy ndarray
1185+
elif hasattr(raw, 'tolist') and hasattr(raw, 'ndim'):
1186+
rows = raw.tolist()
11761187
if cur.description:
11771188
names = [x[0] for x in cur.description]
1178-
if fix_names:
1179-
names = [under2camel(str(x).replace(' ', '')) for x in names]
1180-
out = [{k: v for k, v in zip(names, row)} for row in out]
1189+
out = [
1190+
{k: v for k, v in zip(names, row)}
1191+
for row in rows
1192+
]
1193+
else:
1194+
return []
1195+
# list of tuples/namedtuples/dicts
1196+
else:
1197+
out = list(raw)
1198+
if not out:
1199+
return []
1200+
if isinstance(out[0], dict):
1201+
pass # already dicts
1202+
elif isinstance(out[0], (tuple, list)):
1203+
if cur.description:
1204+
names = [x[0] for x in cur.description]
1205+
out = [
1206+
{k: v for k, v in zip(names, row)}
1207+
for row in out
1208+
]
1209+
else:
1210+
return []
1211+
if not out:
1212+
return []
1213+
# Apply camelCase name conversion if requested
1214+
if fix_names:
1215+
out = [
1216+
{
1217+
under2camel(str(k).replace(' ', '')): v
1218+
for k, v in row.items()
1219+
}
1220+
for row in out
1221+
]
11811222
return out
11821223

11831224
@abc.abstractmethod

0 commit comments

Comments
 (0)