Skip to content
This repository was archived by the owner on Aug 19, 2025. It is now read-only.

Commit 25e65ed

Browse files
authored
Use backend native fetch_val() implementation when available (#132)
* Use backend native fetch_val() implementation when available (fixes fetch_val for raw queries in Postgres) * Also test row[item] to maintain coverage * Improved fetch_val tests readability * Update core.py
1 parent a330b7f commit 25e65ed

File tree

4 files changed

+30
-2
lines changed

4 files changed

+30
-2
lines changed

databases/backends/postgres.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ async def fetch_one(self, query: ClauseElement) -> typing.Optional[typing.Mappin
155155
return None
156156
return Record(row, result_columns, self._dialect)
157157

158+
async def fetch_val(
159+
self, query: ClauseElement, column: typing.Any = 0
160+
) -> typing.Any:
161+
assert self._connection is not None, "Connection is not acquired"
162+
query, args, _ = self._compile(query)
163+
return await self._connection.fetchval(query, *args, column=column)
164+
158165
async def execute(self, query: ClauseElement) -> typing.Any:
159166
assert self._connection is not None, "Connection is not acquired"
160167
query, args, result_columns = self._compile(query)

databases/core.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,7 @@ async def fetch_val(
251251
) -> typing.Any:
252252
built_query = self._build_query(query, values)
253253
async with self._query_lock:
254-
row = await self._connection.fetch_one(built_query)
255-
return None if row is None else row[column]
254+
return await self._connection.fetch_val(built_query, column)
256255

257256
async def execute(
258257
self, query: typing.Union[ClauseElement, str], values: dict = None

databases/interfaces.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ async def fetch_all(self, query: ClauseElement) -> typing.List[typing.Mapping]:
2727
async def fetch_one(self, query: ClauseElement) -> typing.Optional[typing.Mapping]:
2828
raise NotImplementedError() # pragma: no cover
2929

30+
async def fetch_val(
31+
self, query: ClauseElement, column: typing.Any = 0
32+
) -> typing.Any:
33+
row = await self.fetch_one(query)
34+
return None if row is None else row[column]
35+
3036
async def execute(self, query: ClauseElement) -> typing.Any:
3137
raise NotImplementedError() # pragma: no cover
3238

tests/test_databases.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ async def test_queries(database_url):
155155
result = await database.fetch_val(query=query)
156156
assert result == "example1"
157157

158+
# row access (needed to maintain test coverage for Record.__getitem__ in postgres backend)
159+
query = sqlalchemy.sql.select([notes.c.text])
160+
result = await database.fetch_one(query=query)
161+
assert result["text"] == "example1"
162+
assert result[0] == "example1"
163+
158164
# iterate()
159165
query = notes.select()
160166
iterate_results = []
@@ -206,6 +212,16 @@ async def test_queries_raw(database_url):
206212
assert result["text"] == "example2"
207213
assert result["completed"] == False
208214

215+
# fetch_val()
216+
query = "SELECT completed FROM notes WHERE text = :text"
217+
result = await database.fetch_val(query=query, values={"text": "example1"})
218+
assert result == True
219+
query = "SELECT * FROM notes WHERE text = :text"
220+
result = await database.fetch_val(
221+
query=query, values={"text": "example1"}, column="completed"
222+
)
223+
assert result == True
224+
209225
# iterate()
210226
query = "SELECT * FROM notes"
211227
iterate_results = []

0 commit comments

Comments
 (0)