-
-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feature: added async driver support via asynch * added tests for asynch driver * added asynch to docs * fixed sqlalchemy version * fixed docs * changed min python version * updated actions * returned way to import connector to drivers * removed redundant tests * removed unused imports
- Loading branch information
Showing
26 changed files
with
752 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,6 @@ jobs: | |
strategy: | ||
matrix: | ||
python-version: | ||
- "3.6" | ||
- "3.7" | ||
- "3.8" | ||
- "3.9" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import asynch | ||
|
||
from sqlalchemy.sql.elements import TextClause | ||
from sqlalchemy.pool import AsyncAdaptedQueuePool | ||
|
||
from .connector import AsyncAdapt_asynch_dbapi | ||
from ..native.base import ClickHouseDialect_native | ||
|
||
# Export connector version | ||
VERSION = (0, 0, 1, None) | ||
|
||
|
||
class ClickHouseDialect_asynch(ClickHouseDialect_native): | ||
driver = 'asynch' | ||
|
||
is_async = True | ||
supports_statement_cache = True | ||
|
||
@classmethod | ||
def dbapi(cls): | ||
return AsyncAdapt_asynch_dbapi(asynch) | ||
|
||
@classmethod | ||
def get_pool_class(cls, url): | ||
return AsyncAdaptedQueuePool | ||
|
||
def _execute(self, connection, sql, scalar=False, **kwargs): | ||
if isinstance(sql, str): | ||
# Makes sure the query will go through the | ||
# `ClickHouseExecutionContext` logic. | ||
sql = TextClause(sql) | ||
f = connection.scalar if scalar else connection.execute | ||
return f(sql, parameters=kwargs) | ||
|
||
def do_execute(self, cursor, statement, parameters, context=None): | ||
cursor.execute(statement, parameters, context) | ||
|
||
def do_executemany(self, cursor, statement, parameters, context=None): | ||
cursor.executemany(statement, parameters, context) | ||
|
||
|
||
dialect = ClickHouseDialect_asynch |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import asyncio | ||
|
||
from sqlalchemy.engine.interfaces import AdaptedConnection | ||
from sqlalchemy.util.concurrency import await_only | ||
|
||
|
||
class AsyncAdapt_asynch_cursor: | ||
__slots__ = ( | ||
'_adapt_connection', | ||
'_connection', | ||
'await_', | ||
'_cursor', | ||
'_rows' | ||
) | ||
|
||
def __init__(self, adapt_connection): | ||
self._adapt_connection = adapt_connection | ||
self._connection = adapt_connection._connection # noqa | ||
self.await_ = adapt_connection.await_ | ||
|
||
cursor = self._connection.cursor() | ||
|
||
self._cursor = self.await_(cursor.__aenter__()) | ||
self._rows = [] | ||
|
||
@property | ||
def _execute_mutex(self): | ||
return self._adapt_connection._execute_mutex # noqa | ||
|
||
@property | ||
def description(self): | ||
return self._cursor.description | ||
|
||
@property | ||
def rowcount(self): | ||
return self._cursor.rowcount | ||
|
||
@property | ||
def arraysize(self): | ||
return self._cursor.arraysize | ||
|
||
@arraysize.setter | ||
def arraysize(self, value): | ||
self._cursor.arraysize = value | ||
|
||
@property | ||
def lastrowid(self): | ||
return self._cursor.lastrowid | ||
|
||
def close(self): | ||
# note we aren't actually closing the cursor here, | ||
# we are just letting GC do it. to allow this to be async | ||
# we would need the Result to change how it does "Safe close cursor". | ||
self._rows[:] = [] # noqa | ||
|
||
def execute(self, operation, params=None, context=None): | ||
return self.await_(self._execute_async(operation, params, context)) | ||
|
||
async def _execute_async(self, operation, params, context): | ||
async with self._execute_mutex: | ||
result = await self._cursor.execute( | ||
operation, | ||
args=params, | ||
context=context | ||
) | ||
|
||
self._rows = list(await self._cursor.fetchall()) | ||
return result | ||
|
||
def executemany(self, operation, params=None, context=None): | ||
return self.await_(self._executemany_async(operation, params, context)) | ||
|
||
async def _executemany_async(self, operation, params, context): | ||
async with self._execute_mutex: | ||
return await self._cursor.executemany( | ||
operation, | ||
args=params, | ||
context=context | ||
) | ||
|
||
def setinputsizes(self, *args): | ||
pass | ||
|
||
def setoutputsizes(self, *args): | ||
pass | ||
|
||
def __iter__(self): | ||
while self._rows: | ||
yield self._rows.pop(0) | ||
|
||
def fetchone(self): | ||
if self._rows: | ||
return self._rows.pop(0) | ||
else: | ||
return None | ||
|
||
def fetchmany(self, size=None): | ||
if size is None: | ||
size = self.arraysize | ||
|
||
retval = self._rows[0:size] | ||
self._rows[:] = self._rows[size:] | ||
return retval | ||
|
||
def fetchall(self): | ||
retval = self._rows[:] | ||
self._rows[:] = [] | ||
return retval | ||
|
||
|
||
class AsyncAdapt_asynch_dbapi: | ||
def __init__(self, asynch): | ||
self.asynch = asynch | ||
self.paramstyle = 'pyformat' | ||
self._init_dbapi_attributes() | ||
|
||
class Error(Exception): | ||
pass | ||
|
||
def _init_dbapi_attributes(self): | ||
for name in ( | ||
'ServerException', | ||
'UnexpectedPacketFromServerError', | ||
'LogicalError', | ||
'UnknownTypeError', | ||
'ChecksumDoesntMatchError', | ||
'TypeMismatchError', | ||
'UnknownCompressionMethod', | ||
'TooLargeStringSize', | ||
'NetworkError', | ||
'SocketTimeoutError', | ||
'UnknownPacketFromServerError', | ||
'CannotParseUuidError', | ||
'CannotParseDomainError', | ||
'PartiallyConsumedQueryError', | ||
'ColumnException', | ||
'ColumnTypeMismatchException', | ||
'StructPackException', | ||
'InterfaceError', | ||
'DatabaseError', | ||
'ProgrammingError', | ||
'NotSupportedError', | ||
): | ||
setattr(self, name, getattr(self.asynch.errors, name)) | ||
|
||
def connect(self, *args, **kwargs) -> 'AsyncAdapt_asynch_connection': | ||
return AsyncAdapt_asynch_connection( | ||
self, | ||
await_only(self.asynch.connect(*args, **kwargs)) | ||
) | ||
|
||
|
||
class AsyncAdapt_asynch_connection(AdaptedConnection): | ||
await_ = staticmethod(await_only) | ||
__slots__ = ('dbapi', '_execute_mutex') | ||
|
||
def __init__(self, dbapi, connection): | ||
self.dbapi = dbapi | ||
self._connection = connection | ||
self._execute_mutex = asyncio.Lock() | ||
|
||
def ping(self, reconnect): | ||
return self.await_(self._ping_async()) | ||
|
||
async def _ping_async(self): | ||
async with self._execute_mutex: | ||
return await self._connection.ping() | ||
|
||
def character_set_name(self): | ||
return self._connection.character_set_name() | ||
|
||
def autocommit(self, value): | ||
self.await_(self._connection.autocommit(value)) | ||
|
||
def cursor(self, server_side=False): | ||
return AsyncAdapt_asynch_cursor(self) | ||
|
||
def rollback(self): | ||
self.await_(self._connection.rollback()) | ||
|
||
def commit(self): | ||
self.await_(self._connection.commit()) | ||
|
||
def close(self): | ||
self.await_(self._connection.close()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,14 @@ | ||
from sqlalchemy.orm import sessionmaker | ||
from sqlalchemy.orm import sessionmaker, Session | ||
from sqlalchemy.ext.asyncio import AsyncSession | ||
|
||
from .query import Query | ||
|
||
|
||
def make_session(engine): | ||
Session = sessionmaker(bind=engine) | ||
def make_session(engine, is_async=False): | ||
session_class = Session | ||
if is_async: | ||
session_class = AsyncSession | ||
|
||
return Session(query_cls=Query) | ||
factory = sessionmaker(bind=engine, class_=session_class) | ||
|
||
return factory(query_cls=Query) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
License | ||
======= | ||
|
||
clickHouse-sqlalchemy is distributed under the `MIT license | ||
clickhouse-sqlalchemy is distributed under the `MIT license | ||
<http://www.opensource.org/licenses/mit-license.php>`_. | ||
|
Oops, something went wrong.