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

Commit 6d92566

Browse files
author
ansipunk
committed
S01E02
1 parent a86edfa commit 6d92566

File tree

4 files changed

+62
-14
lines changed

4 files changed

+62
-14
lines changed

databases/backends/dialects/psycopg.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,12 @@ def get_dialect() -> Dialect:
5858
return dialect
5959

6060

61-
def compile_query(query: ClauseElement, dialect: Dialect) -> typing.Tuple[str, list, tuple]:
62-
compiled = query.compile(dialect=dialect, compile_kwargs={"render_postcompile": True})
61+
def compile_query(
62+
query: ClauseElement, dialect: Dialect
63+
) -> typing.Tuple[str, list, tuple]:
64+
compiled = query.compile(
65+
dialect=dialect, compile_kwargs={"render_postcompile": True}
66+
)
6367

6468
if not isinstance(query, DDLElement):
6569
compiled_params = sorted(compiled.params.items())

databases/backends/psycopg.py

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ async def connect(self) -> None:
3737
return
3838

3939
self._pool = psycopg_pool.AsyncConnectionPool(
40-
self._database_url.url, open=False, **self._options)
40+
self._database_url._url, open=False, **self._options
41+
)
4142

4243
# TODO: Add configurable timeouts
4344
await self._pool.open()
@@ -86,13 +87,33 @@ async def fetch_all(self, query: ClauseElement) -> typing.List[RecordInterface]:
8687
raise RuntimeError("Connection is not acquired")
8788

8889
query_str, args, result_columns = compile_query(query, self._dialect)
89-
rows = await self._connection.fetch(query_str, *args)
90+
91+
async with self._connection.cursor() as cursor:
92+
await cursor.execute(query_str, args)
93+
rows = await cursor.fetchall()
94+
9095
column_maps = create_column_maps(result_columns)
9196
return [Record(row, result_columns, self._dialect, column_maps) for row in rows]
92-
raise NotImplementedError() # pragma: no cover
9397

9498
async def fetch_one(self, query: ClauseElement) -> typing.Optional[RecordInterface]:
95-
raise NotImplementedError() # pragma: no cover
99+
if self._connection is None:
100+
raise RuntimeError("Connection is not acquired")
101+
102+
query_str, args, result_columns = compile_query(query, self._dialect)
103+
104+
async with self._connection.cursor() as cursor:
105+
await cursor.execute(query_str, args)
106+
row = await cursor.fetchone()
107+
108+
if row is None:
109+
return None
110+
111+
return Record(
112+
row,
113+
result_columns,
114+
self._dialect,
115+
create_column_maps(result_columns),
116+
)
96117

97118
async def fetch_val(
98119
self, query: ClauseElement, column: typing.Any = 0
@@ -101,25 +122,47 @@ async def fetch_val(
101122
return None if row is None else row[column]
102123

103124
async def execute(self, query: ClauseElement) -> typing.Any:
104-
raise NotImplementedError() # pragma: no cover
125+
if self._connection is None:
126+
raise RuntimeError("Connection is not acquired")
127+
128+
query_str, args, _ = compile_query(query, self._dialect)
129+
130+
async with self._connection.cursor() as cursor:
131+
await cursor.execute(query_str, args)
105132

106133
async def execute_many(self, queries: typing.List[ClauseElement]) -> None:
107-
raise NotImplementedError() # pragma: no cover
134+
# TODO: Find a way to use psycopg's executemany
135+
for query in queries:
136+
await self.execute(query)
108137

109138
async def iterate(
110139
self, query: ClauseElement
111140
) -> typing.AsyncGenerator[typing.Mapping, None]:
112-
raise NotImplementedError() # pragma: no cover
113-
# mypy needs async iterators to contain a `yield`
114-
# https://github.com/python/mypy/issues/5385#issuecomment-407281656
115-
yield True # pragma: no cover
141+
if self._connection is None:
142+
raise RuntimeError("Connection is not acquired")
143+
144+
query_str, args, result_columns = compile_query(query, self._dialect)
145+
column_maps = create_column_maps(result_columns)
146+
147+
async with self._connection.cursor() as cursor:
148+
await cursor.execute(query_str, args)
149+
150+
while True:
151+
row = await cursor.fetchone()
152+
153+
if row is None:
154+
break
155+
156+
yield Record(row, result_columns, self._dialect, column_maps)
116157

117158
def transaction(self) -> "TransactionBackend":
118159
raise NotImplementedError() # pragma: no cover
119160

120161
@property
121162
def raw_connection(self) -> typing.Any:
122-
raise NotImplementedError() # pragma: no cover
163+
if self._connection is None:
164+
raise RuntimeError("Connection is not acquired")
165+
return self._connection
123166

124167

125168
class PsycopgTransaction(TransactionBackend):

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ aiosqlite==0.20.0
88
asyncpg==0.29.0
99
psycopg==3.1.18
1010
psycopg-binary==3.1.18
11+
psycopg-pool==3.2.1
1112

1213
# Sync database drivers for standard tooling around setup/teardown/migrations.
1314
psycopg==3.1.18

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def get_packages(package):
5555
"postgresql": ["asyncpg"],
5656
"aiopg": ["aiopg"],
5757
"asyncpg": ["asyncpg"],
58-
"psycopg3": ["psycopg"],
58+
"psycopg3": ["psycopg", "psycopg-pool"],
5959
"sqlite": ["aiosqlite"],
6060
"aiosqlite": ["aiosqlite"],
6161
},

0 commit comments

Comments
 (0)