Skip to content

Commit aff23fe

Browse files
committed
Fix duplicate query output column names for dict rows
1 parent 8d74ffc commit aff23fe

2 files changed

Lines changed: 35 additions & 3 deletions

File tree

sqlite_utils/db.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,23 @@ def quote_identifier(identifier: str) -> str:
8282
return '"{}"'.format(identifier.replace('"', '""'))
8383

8484

85+
def _row_to_dict(keys: Sequence[str], row: Sequence[Any]) -> Dict[str, Any]:
86+
"""
87+
Convert a row plus column names to a dictionary.
88+
89+
Duplicate column names are suffixed with ``_2``, ``_3``... so values are
90+
preserved instead of overwritten.
91+
"""
92+
counts: Dict[str, int] = {}
93+
result: Dict[str, Any] = {}
94+
for key, value in zip(keys, row):
95+
count = counts.get(key, 0) + 1
96+
counts[key] = count
97+
final_key = key if count == 1 else "{}_{}".format(key, count)
98+
result[final_key] = value
99+
return result
100+
101+
85102
try:
86103
import pandas as pd # type: ignore
87104
except ImportError:
@@ -548,7 +565,7 @@ def query(
548565
cursor = self.execute(sql, params or tuple())
549566
keys = [d[0] for d in cursor.description]
550567
for row in cursor:
551-
yield dict(zip(keys, row))
568+
yield _row_to_dict(keys, row)
552569

553570
def execute(
554571
self, sql: str, parameters: Optional[Union[Sequence, Dict[str, Any]]] = None
@@ -1445,7 +1462,7 @@ def rows_where(
14451462
cursor = self.db.execute(sql, where_args or [])
14461463
columns = [c[0] for c in cursor.description]
14471464
for row in cursor:
1448-
yield dict(zip(columns, row))
1465+
yield _row_to_dict(columns, row)
14491466

14501467
def pks_and_rows_where(
14511468
self,
@@ -2862,7 +2879,7 @@ def search(
28622879
)
28632880
columns = [c[0] for c in cursor.description]
28642881
for row in cursor:
2865-
yield dict(zip(columns, row))
2882+
yield _row_to_dict(columns, row)
28662883

28672884
def value_or_default(self, key: str, value: Any) -> Any:
28682885
return self._defaults[key] if value is DEFAULT else value

tests/test_query.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,18 @@ def test_execute_returning_dicts(fresh_db):
1515
assert fresh_db.execute_returning_dicts("select * from test") == [
1616
{"id": 1, "bar": 2}
1717
]
18+
19+
20+
def test_query_duplicate_output_columns_are_suffixed(fresh_db):
21+
fresh_db.execute("create table one (id integer, value text)")
22+
fresh_db.execute("create table two (id integer, value text)")
23+
fresh_db["one"].insert({"id": 1, "value": "left"})
24+
fresh_db["two"].insert({"id": 2, "value": "right"})
25+
26+
rows = list(
27+
fresh_db.query(
28+
"select one.id, two.id, one.value, two.value from one, two where one.id = 1"
29+
)
30+
)
31+
32+
assert rows == [{"id": 1, "id_2": 2, "value": "left", "value_2": "right"}]

0 commit comments

Comments
 (0)