This commit is contained in:
2025-05-22 20:25:38 +02:00
parent 09f6750c2b
commit ce03fbf12f
529 changed files with 3353 additions and 3312 deletions

View File

@@ -44,6 +44,36 @@ User-Defined Functions
aiosqlite extends pysqlite to support async, so we can create our own user-defined functions (UDFs)
in Python and use them directly in SQLite queries as described here: :ref:`pysqlite_udfs`.
.. _aiosqlite_serializable:
Serializable isolation / Savepoints / Transactional DDL (asyncio version)
-------------------------------------------------------------------------
Similarly to pysqlite, aiosqlite does not support SAVEPOINT feature.
The solution is similar to :ref:`pysqlite_serializable`. This is achieved by the event listeners in async::
from sqlalchemy import create_engine, event
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("sqlite+aiosqlite:///myfile.db")
@event.listens_for(engine.sync_engine, "connect")
def do_connect(dbapi_connection, connection_record):
# disable aiosqlite's emitting of the BEGIN statement entirely.
# also stops it from emitting COMMIT before any DDL.
dbapi_connection.isolation_level = None
@event.listens_for(engine.sync_engine, "begin")
def do_begin(conn):
# emit our own BEGIN
conn.exec_driver_sql("BEGIN")
.. warning:: When using the above recipe, it is advised to not use the
:paramref:`.Connection.execution_options.isolation_level` setting on
:class:`_engine.Connection` and :func:`_sa.create_engine`
with the SQLite driver,
as this function necessarily will also alter the ".isolation_level" setting.
""" # noqa
@@ -60,6 +90,9 @@ from ...util.concurrency import await_only
class AsyncAdapt_aiosqlite_cursor:
# TODO: base on connectors/asyncio.py
# see #10415
__slots__ = (
"_adapt_connection",
"_connection",
@@ -151,6 +184,8 @@ class AsyncAdapt_aiosqlite_cursor:
class AsyncAdapt_aiosqlite_ss_cursor(AsyncAdapt_aiosqlite_cursor):
# TODO: base on connectors/asyncio.py
# see #10415
__slots__ = "_cursor"
server_side = True

View File

@@ -215,7 +215,7 @@ by *not even emitting BEGIN* until the first write operation.
SQLite's transactional scope is impacted by unresolved
issues in the pysqlite driver, which defers BEGIN statements to a greater
degree than is often feasible. See the section :ref:`pysqlite_serializable`
for techniques to work around this behavior.
or :ref:`aiosqlite_serializable` for techniques to work around this behavior.
.. seealso::
@@ -273,8 +273,9 @@ won't work at all with pysqlite unless workarounds are taken.
.. warning::
SQLite's SAVEPOINT feature is impacted by unresolved
issues in the pysqlite driver, which defers BEGIN statements to a greater
degree than is often feasible. See the section :ref:`pysqlite_serializable`
issues in the pysqlite and aiosqlite drivers, which defer BEGIN statements
to a greater degree than is often feasible. See the sections
:ref:`pysqlite_serializable` and :ref:`aiosqlite_serializable`
for techniques to work around this behavior.
Transactional DDL
@@ -843,7 +844,7 @@ Reflecting internal schema tables
----------------------------------
Reflection methods that return lists of tables will omit so-called
"SQLite internal schema object" names, which are referred towards by SQLite
"SQLite internal schema object" names, which are considered by SQLite
as any object name that is prefixed with ``sqlite_``. An example of
such an object is the ``sqlite_sequence`` table that's generated when
the ``AUTOINCREMENT`` column parameter is used. In order to return
@@ -1318,6 +1319,9 @@ class SQLiteCompiler(compiler.SQLCompiler):
def visit_char_length_func(self, fn, **kw):
return "length%s" % self.function_argspec(fn)
def visit_aggregate_strings_func(self, fn, **kw):
return "group_concat%s" % self.function_argspec(fn)
def visit_cast(self, cast, **kwargs):
if self.dialect.supports_cast:
return super().visit_cast(cast, **kwargs)
@@ -2444,10 +2448,16 @@ class SQLiteDialect(default.DefaultDialect):
if table_data is None:
# system tables, etc.
return
# note that we already have the FKs from PRAGMA above. This whole
# regexp thing is trying to locate additional detail about the
# FKs, namely the name of the constraint and other options.
# so parsing the columns is really about matching it up to what
# we already have.
FK_PATTERN = (
r"(?:CONSTRAINT (\w+) +)?"
r"FOREIGN KEY *\( *(.+?) *\) +"
r'REFERENCES +(?:(?:"(.+?)")|([a-z0-9_]+)) *\((.+?)\) *'
r'REFERENCES +(?:(?:"(.+?)")|([a-z0-9_]+)) *\( *((?:(?:"[^"]+"|[a-z0-9_]+) *(?:, *)?)+)\) *' # noqa: E501
r"((?:ON (?:DELETE|UPDATE) "
r"(?:SET NULL|SET DEFAULT|CASCADE|RESTRICT|NO ACTION) *)*)"
r"((?:NOT +)?DEFERRABLE)?"