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

Commit cf0b368

Browse files
Merge pull request #58 from gvbgduh/raw_driver_call
Support for `connection.raw_connection`
2 parents 1e0f679 + 101ab4f commit cf0b368

File tree

7 files changed

+110
-1
lines changed

7 files changed

+110
-1
lines changed

databases/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from databases.core import Database, DatabaseURL
22

3-
43
__version__ = "0.1.9"
54
__all__ = ["Database", "DatabaseURL"]

databases/backends/mysql.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ def _compile(
172172
logger.debug("Query: %s\nArgs: %s", compiled.string, args)
173173
return compiled.string, args, CompilationContext(execution_context)
174174

175+
@property
176+
def raw_connection(self) -> aiomysql.connection.Connection:
177+
assert self._connection is not None, "Connection is not acquired"
178+
return self._connection
179+
175180

176181
class MySQLTransaction(TransactionBackend):
177182
def __init__(self, connection: MySQLConnection):

databases/backends/postgres.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ def _compile(self, query: ClauseElement) -> typing.Tuple[str, list, tuple]:
171171
logger.debug("Query: %s\nArgs: %s", compiled_query, args)
172172
return compiled_query, args, compiled._result_columns
173173

174+
@property
175+
def raw_connection(self) -> asyncpg.connection.Connection:
176+
assert self._connection is not None, "Connection is not acquired"
177+
return self._connection
178+
174179

175180
class PostgresTransaction(TransactionBackend):
176181
def __init__(self, connection: PostgresConnection):

databases/backends/sqlite.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ def _compile(
153153
logger.debug("Query: %s\nArgs: %s", compiled.string, args)
154154
return compiled.string, args, CompilationContext(execution_context)
155155

156+
@property
157+
def raw_connection(self) -> aiosqlite.core.Connection:
158+
assert self._connection is not None, "Connection is not acquired"
159+
return self._connection
160+
156161

157162
class SQLiteTransaction(TransactionBackend):
158163
def __init__(self, connection: SQLiteConnection):

databases/core.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ async def iterate(
177177
def transaction(self, *, force_rollback: bool = False) -> "Transaction":
178178
return Transaction(self, force_rollback)
179179

180+
@property
181+
def raw_connection(self) -> typing.Any:
182+
return self._connection.raw_connection
183+
180184

181185
class Transaction:
182186
def __init__(self, connection: Connection, force_rollback: bool) -> None:

databases/interfaces.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ async def iterate(
4444
def transaction(self) -> "TransactionBackend":
4545
raise NotImplementedError() # pragma: no cover
4646

47+
@property
48+
def raw_connection(self) -> typing.Any:
49+
raise NotImplementedError() # pragma: no cover
50+
4751

4852
class TransactionBackend:
4953
async def start(self, is_root: bool) -> None:

tests/test_databases.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,3 +514,90 @@ async def get_connection_2():
514514
test_complete.set()
515515
await task_1
516516
await task_2
517+
518+
519+
@pytest.mark.parametrize("database_url", DATABASE_URLS)
520+
@async_adapter
521+
async def test_connection_context_with_raw_connection(database_url):
522+
"""
523+
Test connection contexts with respect to the raw connection.
524+
"""
525+
async with Database(database_url) as database:
526+
async with database.connection() as connection_1:
527+
async with database.connection() as connection_2:
528+
assert connection_1 is connection_2
529+
assert connection_1.raw_connection is connection_2.raw_connection
530+
531+
532+
@pytest.mark.parametrize("database_url", DATABASE_URLS)
533+
@async_adapter
534+
async def test_queries_with_expose_backend_connection(database_url):
535+
"""
536+
Replication of `execute()`, `execute_many()`, `fetch_all()``, and
537+
`fetch_one()` using the raw driver interface.
538+
"""
539+
async with Database(database_url) as database:
540+
async with database.connection() as connection:
541+
async with database.transaction(force_rollback=True):
542+
# Get the raw connection
543+
raw_connection = connection.raw_connection
544+
545+
# Insert query
546+
if str(database_url).startswith("mysql"):
547+
insert_query = "INSERT INTO notes (text, completed) VALUES (%s, %s)"
548+
else:
549+
insert_query = "INSERT INTO notes (text, completed) VALUES ($1, $2)"
550+
551+
# execute()
552+
values = ("example1", True)
553+
554+
if str(database_url).startswith("postgresql"):
555+
await raw_connection.execute(insert_query, *values)
556+
elif str(database_url).startswith("mysql"):
557+
cursor = await raw_connection.cursor()
558+
await cursor.execute(insert_query, values)
559+
elif str(database_url).startswith("sqlite"):
560+
await raw_connection.execute(insert_query, values)
561+
562+
# execute_many()
563+
values = [("example2", False), ("example3", True)]
564+
565+
if str(database_url).startswith("mysql"):
566+
cursor = await raw_connection.cursor()
567+
await cursor.executemany(insert_query, values)
568+
else:
569+
await raw_connection.executemany(insert_query, values)
570+
571+
# Select query
572+
select_query = "SELECT notes.id, notes.text, notes.completed FROM notes"
573+
574+
# fetch_all()
575+
if str(database_url).startswith("postgresql"):
576+
results = await raw_connection.fetch(select_query)
577+
elif str(database_url).startswith("mysql"):
578+
cursor = await raw_connection.cursor()
579+
await cursor.execute(select_query)
580+
results = await cursor.fetchall()
581+
elif str(database_url).startswith("sqlite"):
582+
results = await raw_connection.execute_fetchall(select_query)
583+
584+
assert len(results) == 3
585+
# Raw output for the raw request
586+
assert results[0][1] == "example1"
587+
assert results[0][2] == True
588+
assert results[1][1] == "example2"
589+
assert results[1][2] == False
590+
assert results[2][1] == "example3"
591+
assert results[2][2] == True
592+
593+
# fetch_one()
594+
if str(database_url).startswith("postgresql"):
595+
result = await raw_connection.fetchrow(select_query)
596+
else:
597+
cursor = await raw_connection.cursor()
598+
await cursor.execute(select_query)
599+
result = await cursor.fetchone()
600+
601+
# Raw output for the raw request
602+
assert result[1] == "example1"
603+
assert result[2] == True

0 commit comments

Comments
 (0)