SQLite


支持 SQLite 数据库。


下表总结了数据库发行版的当前支持级别。


支持的 SQLite 版本


支撑类型


版本


支持的版本

3.12+


尽力而为

3.7.16+


DBAPI 支持


以下 dialect/DBAPI 选项可用。有关连接信息,请参阅各个 DBAPI 部分。


日期和时间类型


SQLite 没有内置的 DATE、TIME 或 DATETIME 类型,而 pysqlite 有 不提供在 Python 之间转换值的开箱即用功能 datetime 对象和 SQLite 支持的格式。SQLAlchemy 自己的 DateTime 和相关类型提供日期格式 以及使用 SQLite 时的解析功能。实现类包括 DATETIME、DATETIME。这些类型将日期和时间表示为 ISO 格式的字符串,这也很好地支持排序。这些函数不依赖于典型的 “libc” 内部结构,因此完全支持历史日期。


确保文本亲和性¶


为这些类型渲染的 DDL 是标准的 DATETIMEDATETIME 指示器。但是,自定义存储格式也可以应用于这些类型。当检测到存储格式不包含字母字符时,这些类型的 DDL 将呈现为 DATE_CHARTIME_CHARDATETIME_CHAR,以便该列继续具有文本相关性。


另请参阅


键入 Affinity - 在 SQLite 文档中


SQLite 自动递增行为


SQLite 自动递增的背景在: https://sqlite.org/autoinc.html


关键概念:


  • SQLite 具有隐式的“自动递增”功能,该功能适用于专门使用“INTEGER PRIMARY KEY”作为类型 + 主键创建的任何非复合主键列。


  • SQLite 还有一个显式的“AUTOINCREMENT”关键字,即 等效于隐式 autoincrement 功能;此关键字不是 推荐用于一般用途。 SQLAlchemy 不呈现此关键字 除非使用特殊的 SQLite 特定指令(见下文)。 然而 它仍然要求列的类型命名为 “INTEGER”。


使用 AUTOINCREMENT 关键字


要在渲染 DDL 时在主键列上专门渲染 AUTOINCREMENT 关键字,请将标志 sqlite_autoincrement=True 添加到 Table 构造中:

Table(
    "sometable",
    metadata,
    Column("id", Integer, primary_key=True),
    sqlite_autoincrement=True,
)


允许 Integer/INTEGER 以外的 SQLAlchemy 类型自动递增¶


SQLite 的类型模型基于命名约定。除其他外,这意味着任何包含子字符串 “INT” 的类型名称都将被确定为“整数亲和性”。名为 “BIGINT” 的类型, “SPECIAL_INT”甚至“XYZINTQPR”将被SQLite视为具有“整数”亲和性。但是,SQLite 自动递增功能,无论 隐式或显式启用,则要求列类型的 name 正好是字符串 “INTEGER”。因此,如果应用程序使用BigInteger之类的类型作为主键,则在SQLite上,在发出初始CREATE时,此类型将需要呈现为名称“ INTEGER” 桌子语句,以便 autoincrement 行为可用。


实现此目的的一种方法是仅使用 TypeEngine.with_variant() 在 SQLite 上使用 Integer

table = Table(
    "my_table",
    metadata,
    Column(
        "id",
        BigInteger().with_variant(Integer, "sqlite"),
        primary_key=True,
    ),
)


另一种是使用 BigInteger 的子类,当针对 SQLite 编译时,该子类会将其 DDL 名称覆盖为 INTEGER

from sqlalchemy import BigInteger
from sqlalchemy.ext.compiler import compiles


class SLBigInteger(BigInteger):
    pass


@compiles(SLBigInteger, "sqlite")
def bi_c(element, compiler, **kw):
    return "INTEGER"


@compiles(SLBigInteger)
def bi_c(element, compiler, **kw):
    return compiler.visit_BIGINT(element, **kw)


table = Table(
    "my_table", metadata, Column("id", SLBigInteger(), primary_key=True)
)


数据库锁定行为 / 并发


SQLite 不是为高级别的写入并发而设计的。数据库本身作为一个文件,在事务中的写入作期间被完全锁定,这意味着在此期间只有一个 “连接” (实际上是文件句柄) 对数据库具有独占访问权限 - 在此期间所有其他 “连接” 都将被阻止。


Python DBAPI 规范还要求始终在事务中的连接模型;没有 connection.begin() 方法,只有 connection.commit()connection.rollback(),此时将立即开始新的事务。这似乎意味着 SQLite 驱动程序理论上在任何时候都只允许特定数据库文件上使用单个文件句柄。但是,SQLite 本身以及 pysqlite 驱动程序中都有几个因素显着放宽了此限制。


但是,无论使用哪种锁定模式,一旦事务启动并且至少已发出DML(例如INSERT,UPDATE,DELETE),SQLite仍将始终锁定数据库文件,这将阻止其他事务至少在它们也尝试发出DML时。默认情况下,此数据块上的时间长度非常短,然后超时并显示错误。


当与 SQLAlchemy ORM 结合使用时,此行为变得更加关键。默认情况下,SQLAlchemy 的 Session 对象在事务中运行,并且使用其自动刷新模型,可以在任何 SELECT 语句之前发出 DML。这可能会导致 SQLite 数据库的锁定速度比预期的要快。SQLite 和 pysqlite 驱动程序的锁定模式可以在一定程度上进行纵,但是应该注意的是,使用 SQLite 实现高度的写入并发是一场失败的战斗。


有关 SQLite 在设计上缺乏写入并发的更多信息,请 看 其他 RDBMS 可能工作得更好的情况 - 页面底部附近的高并发性。


以下小节介绍了受 SQLite 基于文件的架构影响的领域,此外,在使用 pysqlite 驱动程序时通常需要解决方法才能工作。


事务隔离级别 / 自动提交


SQLite 以非标准的方式支持“事务隔离”,以及两个 轴。 一个是 PRAGMA read_uncommitted 指令。 这个设置基本上可以在其 SERIALIZABLE 隔离的默认模式,以及通常称为 READ UNCOMMITTED 的“脏读”隔离模式。


SQLAlchemy 使用 create_engine.isolation_level 参数 create_engine() 中。与 SQLite 一起使用时,此参数的有效值为 “SERIALIZABLE”“READ UNCOMMITTED” 分别对应于值 0 和 1。SQLite 默认为 SERIALIZABLE,但其行为受 pysqlite 驱动程序的默认行为的影响。


使用 pysqlite 驱动程序时,“AUTOCOMMIT”隔离级别也可用,这将使用 .isolation_level 属性,并在持续时间内将其设置为 None 的设置。


1.3.16 版本中的新功能:在使用 pysqlite / sqlite3 SQLite 驱动程序时添加了对 SQLite AUTOCOMMIT 隔离级别的支持。


SQLite的事务锁定受到影响的另一个轴是通过所使用的BEGIN语句的性质。 三个品种 分别是 “deferred”、“immediate” 和 “exclusive”,如 BEGIN TRANSACTION。 A 顺子 BEGIN 语句使用“延迟”模式,其中数据库文件在第一次读取或写入作之前不会被锁定,并且读取访问在第一次写入作之前对其他事务保持开放。但同样,需要注意的是,pysqlite 驱动程序甚至在第一次写入作之前都不会发出 BEGIN 来干扰此行为。


警告


SQLite 的事务范围受到 pysqlite 驱动程序中未解决的问题的影响,该驱动程序将 BEGIN 语句延迟的程度比通常可行的程度更大。请参阅可序列化隔离 / 保存点 / 事务性 DDL 部分 或 Serializable isolation / Savepoints / Transactional DDL (asyncio version) 以获取解决此行为的技术。


插入/更新/删除...返回


SQLite 方言支持 SQLite 3.35 的 INSERT|UPDATE|DELETE..RETURNING 语法。 插入。。但是,在某些情况下,可以自动使用 RETURNING,以代替使用 cursor.lastrowid 的传统方法 cursor.lastrowid 目前仍然是简单单语句情况的首选,因为它具有更好的性能。


要指定显式 RETURNING 子句,请使用 _UpdateBase.returning() 方法:

# INSERT..RETURNING
result = connection.execute(
    table.insert().values(name="foo").returning(table.c.col1, table.c.col2)
)
print(result.all())

# UPDATE..RETURNING
result = connection.execute(
    table.update()
    .where(table.c.name == "foo")
    .values(name="bar")
    .returning(table.c.col1, table.c.col2)
)
print(result.all())

# DELETE..RETURNING
result = connection.execute(
    table.delete()
    .where(table.c.name == "foo")
    .returning(table.c.col1, table.c.col2)
)
print(result.all())


2.0 版本中的新功能: 添加了对 SQLite RETURNING 的支持


SAVEPOINT 支持


SQLite 支持 SAVEPOINT,它仅在事务 开始。 SQLAlchemy 的 SAVEPOINT 支持可通过 Connection.begin_nested() 方法,以及 Session.begin_nested() 在 ORM 级别。但是,除非采取解决方法,否则 SAVEPOINT 根本无法与 pysqlite 一起使用。


警告


SQLite 的 SAVEPOINT 功能受到未解析的影响 pysqlite 和 aiosqlite 驱动程序中延迟 BEGIN 语句的问题 程度比通常可行的程度更大。查看各部分 可序列化隔离 / 保存点 / 事务性 DDL可序列化隔离 / 保存点 / 事务性 DDL(asyncio 版本) 以获取解决此行为的技术。


事务性 DDL


SQLite 数据库也支持事务性 DDL。在这种情况下,pysqlite 驱动程序不仅无法启动事务,还会在检测到 DDL 时结束任何现有事务,因此需要再次采取解决方法。


警告


SQLite 的事务性 DDL 受到 pysqlite 驱动程序中未解决的问题的影响,该驱动程序无法发出 BEGIN,并且在遇到 DDL 时还会强制 COMMIT 取消任何事务。请参阅可序列化隔离 / 保存点 / 事务性 DDL 部分 以获取解决此行为的技术。


外键支持


SQLite 在为表发出 CREATE 语句时支持 FOREIGN KEY 语法,但是默认情况下,这些约束对表的作没有影响。


SQLite 上的约束检查有三个先决条件:


  • 至少必须使用 SQLite 的 3.6.19 版本


  • 必须在不启用 SQLITE_OMIT_FOREIGN_KEY 或 SQLITE_OMIT_TRIGGER 符号的情况下编译 SQLite 库。


  • foreign_keys 必须在所有 connections before use – 包括对 MetaData.create_all() 中。


SQLAlchemy 允许通过使用事件为新连接自动发出 PRAGMA 语句:

from sqlalchemy.engine import Engine
from sqlalchemy import event


@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
    cursor = dbapi_connection.cursor()
    cursor.execute("PRAGMA foreign_keys=ON")
    cursor.close()


警告


启用 SQLite 外键后,这是不可能的 为包含 相互依赖的外键约束; 要发出这些表的 DDL,需要使用 ALTER TABLE 来执行 单独创建或删除这些约束,SQLite 具有 不支持。


另请参阅


SQLite 外键支持 - 在 SQLite 网站上。


事件 - SQLAlchemy 事件 API。


通过 ALTER 创建/删除外键约束 - 有关 SQLAlchemy 处理工具的更多信息


相互依赖的外键约束。


ON CONFLICT 对约束的支持


另请参阅


本节介绍 SQLite 的“ON CONFLICT”的 DDL 版本,它出现在 CREATE TABLE 语句中。有关应用于 INSERT 语句的 “ON CONFLICT” ,请参阅 INSERT...冲突 (Upsert)。


SQLite 支持一种称为 ON CONFLICT 的非标准 DDL 子句,该子句可以应用于主键、唯一、检查和非空约束。在 DDL 中,它要么在 “CONSTRAINT” 子句中呈现,要么在列定义本身中呈现,具体取决于目标约束的位置。要在 DDL 中呈现此子句,扩展参数 sqlite_on_conflict 可以是 使用 String Conflict Resolution 算法在 PrimaryKeyConstraintUniqueConstraintCheckConstraint 对象。在 Column 对象中,有单独的参数sqlite_on_conflict_not_null sqlite_on_conflict_primary_keysqlite_on_conflict_unique,每个约束类型对应于可从 Column 对象指示的三种类型的相关约束类型。


另请参阅


ON CONFLICT - 在 SQLite 文档中


在 1.3 版本加入.


sqlite_on_conflict参数接受一个字符串参数,该参数只是要选择的解析名称,在SQLite上可以是ROLLBACK,ABORT,FAIL,IGNORE和REPLACE之一。例如,要添加指定 IGNORE 算法的 UNIQUE 约束:

some_table = Table(
    "some_table",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("data", Integer),
    UniqueConstraint("id", "data", sqlite_on_conflict="IGNORE"),
)


上面将 CREATE TABLE DDL 渲染为:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER,
    PRIMARY KEY (id),
    UNIQUE (id, data) ON CONFLICT IGNORE
)


使用 Column.unique 时 标志添加 UNIQUE 约束 添加到单个列,sqlite_on_conflict_unique 参数也可以添加到 Column 中,该参数将被添加到 DDL 中的 UNIQUE 约束中:

some_table = Table(
    "some_table",
    metadata,
    Column("id", Integer, primary_key=True),
    Column(
        "data", Integer, unique=True, sqlite_on_conflict_unique="IGNORE"
    ),
)


渲染:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER,
    PRIMARY KEY (id),
    UNIQUE (data) ON CONFLICT IGNORE
)


要将 FAIL 算法应用于 NOT NULL 约束, sqlite_on_conflict_not_null 用于:

some_table = Table(
    "some_table",
    metadata,
    Column("id", Integer, primary_key=True),
    Column(
        "data", Integer, nullable=False, sqlite_on_conflict_not_null="FAIL"
    ),
)


这将呈现列内联 ON CONFLICT 短语:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    data INTEGER NOT NULL ON CONFLICT FAIL,
    PRIMARY KEY (id)
)


同样,对于内联主键,请使用 sqlite_on_conflict_primary_key

some_table = Table(
    "some_table",
    metadata,
    Column(
        "id",
        Integer,
        primary_key=True,
        sqlite_on_conflict_primary_key="FAIL",
    ),
)


SQLAlchemy 单独呈现 PRIMARY KEY 约束,因此冲突解决算法应用于约束本身:

CREATE TABLE some_table (
    id INTEGER NOT NULL,
    PRIMARY KEY (id) ON CONFLICT FAIL
)


插入。。。冲突时 (Upsert)


另请参阅


本节介绍 SQLite 的 “ON CONFLICT” 的 DML 版本,它出现在 INSERT 语句中。有关应用于 CREATE TABLE 语句的 “ON CONFLICT” ,请参阅约束的 ON CONFLICT 支持


从版本3.24.0开始,SQLite支持通过INSERTON CONFLICT子句将行“更新插入”(更新或插入)到表中 陈述。仅当该行不违反时,才会插入候选行 任何唯一键或主键约束。在唯一约束冲突的情况下, 可以发生 secondary作,可以是 “DO UPDATE”,表示 目标行中的数据应该被更新,或者 “DO NOTHING”,这表示 以静默方式跳过此行。


冲突是使用属于现有唯一约束和索引的列来确定的。这些约束是通过声明组成索引的列和条件来标识的。


SQLAlchemy 通过特定于 SQLite 的 insert() 函数,该函数提供生成方法 Insert.on_conflict_do_update() 以及 Insert.on_conflict_do_nothing()

>>> from sqlalchemy.dialects.sqlite import insert

>>> insert_stmt = insert(my_table).values(
...     id="some_existing_id", data="inserted value"
... )

>>> do_update_stmt = insert_stmt.on_conflict_do_update(
...     index_elements=["id"], set_=dict(data="updated value")
... )

>>> print(do_update_stmt)
INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET data = ?
>>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing(index_elements=["id"]) >>> print(do_nothing_stmt)
INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT (id) DO NOTHING


在 1.4 版本加入.


另请参阅


Upsert - 在 SQLite 文档中。


指定目标


这两种方法都使用列推理提供冲突的 “目标”:


  • Insert.on_conflict_do_update.index_elements 参数指定包含字符串列名称 Column 的序列 对象和/或 SQL 表达式元素,用于标识唯一索引 或 unique 约束。


  • 使用 Insert.on_conflict_do_update.index_elements 要推断索引,可以通过同时指定 Insert.on_conflict_do_update.index_where 参数:

    >>> stmt = insert(my_table).values(user_email="a@b.com", data="inserted data")
    
    >>> do_update_stmt = stmt.on_conflict_do_update(
    ...     index_elements=[my_table.c.user_email],
    ...     index_where=my_table.c.user_email.like("%@gmail.com"),
    ...     set_=dict(data=stmt.excluded.data),
    ... )
    
    >>> print(do_update_stmt)
    
    INSERT INTO my_table (data, user_email) VALUES (?, ?) ON CONFLICT (user_email) WHERE user_email LIKE '%@gmail.com' DO UPDATE SET data = excluded.data


SET 子句


关于冲突...DO UPDATE 用于执行已 existing row, 使用新值和值的任意组合 从提议的插入。这些值是使用 Insert.on_conflict_do_update.set_ 参数。此参数接受一个字典,该字典由 UPDATE 的直接值组成:

>>> stmt = insert(my_table).values(id="some_id", data="inserted value")

>>> do_update_stmt = stmt.on_conflict_do_update(
...     index_elements=["id"], set_=dict(data="updated value")
... )

>>> print(do_update_stmt)
INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT (id) DO UPDATE SET data = ?


警告


该方法 Insert.on_conflict_do_update() 考虑 Python 端的默认 UPDATE 值或生成函数,例如使用 Column.onupdate 指定的值。这些 不会对 UPDATE 的 ON CONFLICT 样式执行值,除非 它们是在 Insert.on_conflict_do_update.set_ 字典。


使用排除的 INSERT 值进行更新


为了引用建议的插入行,特殊别名 Insert.excluded 可用作 Insert 对象的一个属性;此对象在列上创建一个 “excluded.” 前缀,该前缀通知 DO UPDATE 使用如果约束未失败时将插入的值更新该行:

>>> stmt = insert(my_table).values(
...     id="some_id", data="inserted value", author="jlh"
... )

>>> do_update_stmt = stmt.on_conflict_do_update(
...     index_elements=["id"],
...     set_=dict(data="updated value", author=stmt.excluded.author),
... )

>>> print(do_update_stmt)
INSERT INTO my_table (id, data, author) VALUES (?, ?, ?) ON CONFLICT (id) DO UPDATE SET data = ?, author = excluded.author


其他 WHERE 标准


该方法 Insert.on_conflict_do_update() 还接受使用 Insert.on_conflict_do_update.where 参数,它将限制接收 UPDATE 的行:

>>> stmt = insert(my_table).values(
...     id="some_id", data="inserted value", author="jlh"
... )

>>> on_update_stmt = stmt.on_conflict_do_update(
...     index_elements=["id"],
...     set_=dict(data="updated value", author=stmt.excluded.author),
...     where=(my_table.c.status == 2),
... )
>>> print(on_update_stmt)
INSERT INTO my_table (id, data, author) VALUES (?, ?, ?) ON CONFLICT (id) DO UPDATE SET data = ?, author = excluded.author WHERE my_table.status = ?


用 DO NOTHING 跳过行


如果发生与唯一约束的任何冲突,则 ON CONFLICT 可用于完全跳过插入行;下面是使用 Insert.on_conflict_do_nothing() 该方法进行的说明:

>>> stmt = insert(my_table).values(id="some_id", data="inserted value")
>>> stmt = stmt.on_conflict_do_nothing(index_elements=["id"])
>>> print(stmt)
INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT (id) DO NOTHING


如果在未指定任何列或约束的情况下使用 DO NOTHING,则对于发生的任何唯一冲突,它都会跳过 INSERT:

>>> stmt = insert(my_table).values(id="some_id", data="inserted value")
>>> stmt = stmt.on_conflict_do_nothing()
>>> print(stmt)
INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT DO NOTHING


类型反射


SQLite 类型与大多数其他数据库后端的类型不同,因为该类型的字符串名称通常不以一对一的方式对应于“类型”。相反,SQLite 根据类型的字符串匹配模式将每列键入行为链接到五个所谓的“类型亲和力”之一。


SQLAlchemy 的反射过程,在检查类型时,使用一个简单的 查找表将返回的关键字链接到提供的 SQLAlchemy 类型。 这个查找表存在于 SQLite 方言中,因为它适用于所有 其他方言。 但是,SQLite 方言具有不同的“后备” routine 用于特定类型名称未位于查找映射中时; 相反,它实现了位于 https://www.sqlite.org/datatype3.html 第 2.1 节。


提供的 typemap 将与以下类型的确切字符串名称进行直接关联:


BIGINT、BLOB、 BOOLEAN、BOOLEAN、 字符日期日期时间浮点数DECIMAL、FLOAT、 INTEGER、 INTEGER、 数值实数SMALLINT、文本、 时间时间戳瓦尔查尔, NVARCHAR非查尔


当类型名称与上述类型之一不匹配时,将改用 “type affinity” 查找:


  • 如果类型名称包含字符串 INT,则返回 INTEGER


  • 如果类型名称包含字符串 CHAR、CLOBTEXT,则返回 TEXT


  • 如果类型名称包含字符串 BLOB,则返回 NullType


  • 如果类型名称包含字符串 REAL、FLOADOUB


  • 否则,使用 NUMERIC 类型。


部分索引


部分索引,例如使用 WHERE 子句的索引,可以使用 DDL 系统使用参数 sqlite_where 指定:

tbl = Table("testtbl", m, Column("data", Integer))
idx = Index(
    "test_idx1",
    tbl.c.data,
    sqlite_where=and_(tbl.c.data > 5, tbl.c.data < 10),
)


索引将在创建时呈现为:

CREATE INDEX test_idx1 ON testtbl (data)
WHERE data > 5 AND data < 10


带点的列名


使用明确包含句点的表名或列名是 不推荐。虽然这通常对于关系数据库来说通常是一个坏主意,因为点在语法上是一个重要的字符,但直到 SQLite 3.10.0 版的 SQLite 驱动程序都有一个错误,它要求 SQLAlchemy 在结果集中过滤掉这些点。


这个错误完全在 SQLAlchemy 之外,可以这样说明:

import sqlite3

assert sqlite3.sqlite_version_info < (
    3,
    10,
    0,
), "bug is fixed in this version"

conn = sqlite3.connect(":memory:")
cursor = conn.cursor()

cursor.execute("create table x (a integer, b integer)")
cursor.execute("insert into x (a, b) values (1, 1)")
cursor.execute("insert into x (a, b) values (2, 2)")

cursor.execute("select x.a, x.b from x")
assert [c[0] for c in cursor.description] == ["a", "b"]

cursor.execute(
    """
    select x.a, x.b from x where a=1
    union
    select x.a, x.b from x where a=2
    """
)
assert [c[0] for c in cursor.description] == ["a", "b"], [
    c[0] for c in cursor.description
]


第二个断言失败:

Traceback (most recent call last):
  File "test.py", line 19, in <module>
    [c[0] for c in cursor.description]
AssertionError: ['x.a', 'x.b']


在上面,驱动程序错误地报告了列的名称,包括表的名称,这与 UNION 不存在时完全不一致。


SQLAlchemy 依赖于列名在与原始语句的匹配方式上是可预测的,因此 SQLAlchemy 方言别无选择,只能过滤掉这些:

from sqlalchemy import create_engine

eng = create_engine("sqlite://")
conn = eng.connect()

conn.exec_driver_sql("create table x (a integer, b integer)")
conn.exec_driver_sql("insert into x (a, b) values (1, 1)")
conn.exec_driver_sql("insert into x (a, b) values (2, 2)")

result = conn.exec_driver_sql("select x.a, x.b from x")
assert result.keys() == ["a", "b"]

result = conn.exec_driver_sql(
    """
    select x.a, x.b from x where a=1
    union
    select x.a, x.b from x where a=2
    """
)
assert result.keys() == ["a", "b"]


请注意,在上面,即使 SQLAlchemy 过滤掉了点,但 名称仍可寻址

>>> row = result.first()
>>> row["a"]
1
>>> row["x.a"]
1
>>> row["b"]
1
>>> row["x.b"]
1


因此,SQLAlchemy 应用的解决方法仅影响 公共 API 中的 CursorResult.keys()Row.keys() 中。在非常特殊的情况下,应用程序被迫使用包含点的列名,以及 CursorResult.keys()Row.keys() 需要返回这些带点的名称,则可以提供sqlite_raw_colnames执行选项,基于每个 Connection

result = conn.execution_options(sqlite_raw_colnames=True).exec_driver_sql(
    """
    select x.a, x.b from x where a=1
    union
    select x.a, x.b from x where a=2
    """
)
assert result.keys() == ["x.a", "x.b"]


或按引擎

engine = create_engine(
    "sqlite://", execution_options={"sqlite_raw_colnames": True}
)


使用 per-Engine 执行选项时,请注意, 使用 UNION 的 Core 和 ORM 查询可能无法正常工作


特定于 SQLite 的表选项


SQLite 方言与 Table 构造直接支持 CREATE TABLE 的一个选项:


  • 没有 ROWID

    Table("some_table", metadata, ..., sqlite_with_rowid=False)

  • 严格

    Table("some_table", metadata, ..., sqlite_strict=True)


    2.0.37 新版功能.


镜像内部 schema 表


返回表列表的反射方法将省略所谓的“SQLite 内部架构对象”名称,SQLite 将其视为任何前缀为 sqlite_ 的对象名称。此类对象的一个示例是在使用 AUTOINCREMENT 列参数时生成的 sqlite_sequence 表。为了返回这些对象,可以将参数 sqlite_include_internal=True 传递给 MetaData.reflect()Inspector.get_table_names()


2.0 版本中的新功能: 添加了 sqlite_include_internal=True 参数。以前,SQLAlchemy 反射方法不会忽略这些表。


注意


sqlite_include_internal 参数不引用 sqlite_master 等架构中存在的“系统”表。


另请参阅


SQLite 内部架构对象 - 在 SQLite 文档中。


SQLite 数据类型


与所有 SQLAlchemy 方言一样,所有已知对 SQLite 有效的 UPPERCASE 类型都可以从顶级方言导入,无论它们来自 sqlalchemy.types 还是本地方言:

from sqlalchemy.dialects.sqlite import (
    BLOB,
    BOOLEAN,
    CHAR,
    DATE,
    DATETIME,
    DECIMAL,
    FLOAT,
    INTEGER,
    NUMERIC,
    JSON,
    SMALLINT,
    TEXT,
    TIME,
    TIMESTAMP,
    VARCHAR,
)

对象名称

描述

DATE


使用字符串在 SQLite 中表示 Python 日期对象。

DATETIME


使用字符串在 SQLite 中表示 Python datetime 对象。

JSON


SQLite JSON 类型。

TIME


使用字符串在 SQLite 中表示 Python 时间对象。


sqlalchemy.dialects.sqlite。日期时间


使用字符串在 SQLite 中表示 Python datetime 对象。


默认字符串存储格式为:

"%(year)04d-%(month)02d-%(day)02d %(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"


例如:

2021-03-15 12:05:57.105542


默认情况下,使用 Python datetime.fromisoformat() 函数解析传入的存储格式。


在 2.0 版本发生变更: datetime.fromisoformat() 用于默认的日期时间字符串解析。


存储格式可以在一定程度上使用 storage_formatregexp 参数,例如:

import re
from sqlalchemy.dialects.sqlite import DATETIME

dt = DATETIME(
    storage_format=(
        "%(year)04d/%(month)02d/%(day)02d %(hour)02d:%(minute)02d:%(second)02d"
    ),
    regexp=r"(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)",
)

参数

  • storage_format – 将应用于 dict 的格式字符串,键为 year、month、day、hour、minute、second 和 microsecond。


  • regexp – 将应用于传入结果行的正则表达式,取代 datetime.fromisoformat() 来解析传入的 字符串。如果 regexp 包含命名组,则生成的 match dict 为 作为关键字参数应用于 Python datetime() 构造函数。 否则,如果使用位置组,则 datetime() 构造函数 通过 *map(int, match_obj.groups(0)) .


类签名


sqlalchemy.dialects.sqlite.DATETIME sqlalchemy.dialects.sqlite.base._DateTimeMixinsqlalchemy.types.DateTime


sqlalchemy.dialects.sqlite。日期


使用字符串在 SQLite 中表示 Python 日期对象。


默认字符串存储格式为:

"%(year)04d-%(month)02d-%(day)02d"


例如:

2011-03-15


默认情况下,使用 Python date.fromisoformat() 函数解析传入的存储格式。


在 2.0 版本发生变更: date.fromisoformat() 用于默认日期字符串解析。


存储格式可以在一定程度上使用 storage_formatregexp 参数,例如:

import re
from sqlalchemy.dialects.sqlite import DATE

d = DATE(
    storage_format="%(month)02d/%(day)02d/%(year)04d",
    regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)"),
)

参数

  • storage_format – 格式字符串,该字符串将应用于键为 year、month 和 day 的字典。


  • regexp – 将应用于传入结果行的正则表达式,将 date.fromisoformat() 替换为 解析传入字符串。如果 regexp 包含命名组,则生成的 match dict 作为关键字应用于 Python date() 构造函数 参数。否则,如果使用位置组,则 date() constructor 使用位置参数通过 *map(int, match_obj.groups(0)) .


类签名


sqlalchemy.dialects.sqlite.DATE sqlalchemy.dialects.sqlite.base._DateTimeMixinsqlalchemy.types.Date


sqlalchemy.dialects.sqlite。JSON格式¶


SQLite JSON 类型。


SQLite 通过其 JSON3.9 扩展支持 JSON。请注意,JSON1 是一个 loadable 扩展,因此可能不可用,或者可能需要运行时加载。


每当 base JSON 数据类型用于 SQLite 后端。


另请参阅


JSON - 通用跨平台 JSON 数据类型的主要文档。


JSON 类型支持 JSON 值的持久化以及 JSON 提供的核心索引作 datatype 来渲染 JSON_EXTRACT 函数包装在数据库级别的 JSON_QUOTE 函数中。提取的值会引用,以确保结果始终是 JSON 字符串值。


在 1.3 版本加入.


方法 sqlalchemy.dialects.sqlite.JSON. __init__none_as_null: bool = False


继承自 sqlalchemy.types.JSON.__init__ JSON 的方法


构造 JSON 类型。


参数


none_as_null=假


如果为 True,则将值 None 保留为 SQL NULL 值,而不是 null 的 JSON 编码。请注意,当此标志为 False 时,null() 构造仍可用于保留 NULL 值,该值可以直接作为参数值传递,该参数值由 JSON 类型专门解释为 SQL NULL:

from sqlalchemy import null

conn.execute(table.insert(), {"data": null()})


注意


JSON.none_as_null 不适用于传递给 Column.defaultColumn.server_default;值为 None 为这些参数传递的表示 “No default present”。


此外,在 SQL 比较表达式中使用时,Python 值 None 继续引用 SQL null,而不是 JSON NULL。JSON.none_as_null 标志显式引用 INSERT 或 UPDATE 语句中值的持久性JSON.零 value 应用于希望比较的 SQL 表达式 JSON null 的 JSON null。


sqlalchemy.dialects.sqlite。时间


使用字符串在 SQLite 中表示 Python 时间对象。


默认字符串存储格式为:

"%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"


例如:

12:05:57.10558


默认情况下,使用 Python time.fromisoformat() 函数解析传入的存储格式。


在 2.0 版本发生变更: time.fromisoformat() 用于默认时间字符串解析。


存储格式可以在一定程度上使用 storage_formatregexp 参数,例如:

import re
from sqlalchemy.dialects.sqlite import TIME

t = TIME(
    storage_format="%(hour)02d-%(minute)02d-%(second)02d-%(microsecond)06d",
    regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?"),
)

参数

  • storage_format – 格式字符串,该字符串将应用于键 hour、minute、second 和 microsecond。


  • regexp—— 将应用于传入结果行的正则表达式,取代 datetime.fromisoformat() 来解析传入字符串的使用。如果 regexp 包含命名组,则生成的 match dict 将作为关键字参数应用于 Python time() 构造函数。否则,如果使用位置组,则通过 *map(int, match_obj.groups(0)) .


类签名


sqlalchemy.dialects.sqlite.TIME sqlalchemy.dialects.sqlite.base._DateTimeMixinsqlalchemy.types.Time


SQLite DML 结构


对象名称

描述


insert(表)


构造特定于 sqlite 的变体 Insert 构建。


插入


特定于 SQLite 的 INSERT 实现。


函数 sqlalchemy.dialects.sqlite。inserttable _DMLTableArgument 插入


构造特定于 sqlite 的变体 Insert 构建。


sqlalchemy.dialects.sqlite.insert() 函数创建一个 sqlalchemy.dialects.sqlite.Insert .此类基于与方言无关的 Insert 结构,该结构可以使用 SQLAlchemy Core 中的 insert() 函数构造。


Insert 构造包括其他方法 Insert.on_conflict_do_update() Insert.on_conflict_do_nothing()


sqlalchemy.dialects.sqlite。插入


特定于 SQLite 的 INSERT 实现。


为特定于 SQLite 的语法添加方法,例如 ON CONFLICT。


Insert 对象是使用 sqlalchemy.dialects.sqlite.insert() 功能。


在 1.4 版本加入.


attribute excluded不包括属性 sqlalchemy.dialects.sqlite.Insert. )¶


为 ON CONFLICT 语句提供排除的命名空间


SQLite 的 ON CONFLICT 子句允许引用将要插入的行,称为 excluded。此属性提供此行中的所有列以供引用。


提示


Insert.excluded 属性是 ColumnCollection 的一个实例,它提供的接口与 Table.c 的接口相同 访问表和列中描述的集合。在这个集合中,普通名称可以像属性一样访问(例如 stmt.excluded.some_column),但特殊名称和字典方法名称应该使用索引访问来访问,例如 stmt.excluded[“column name”]stmt.excluded[“values”] 中。 请参阅 docsstring 的 ColumnCollection 获取更多示例。


属性 sqlalchemy.dialects.sqlite.Insert. inherit_cache:boolNone = False


指示此 HasCacheKey 实例是否应使用其直接超类使用的缓存键生成方案。


该属性默认为 None,这表示构造尚未考虑是否适合参与缓存;这在功能上等效于将值设置为 False,但还会发出警告。


如果与对象对应的 SQL 不基于此类的本地属性而不是其超类而更改,则可以在特定类上将此标志设置为 True


另请参阅


启用对自定义构造的缓存支持 - 设置 HasCacheKey.inherit_cache 第三方或用户定义的 SQL 构造的属性。


方法 sqlalchemy.dialects.sqlite.Insert. on_conflict_do_nothingindex_elements:Iterable[Column[Any]strDDLConstraintColumnRole]None=None, index_where:WhereHavingRoleNone=None) Self


为 ON CONFLICT 子句指定 DO NOTHING作。


参数

  • index_elements– 由字符串 column names、Column 组成的序列 对象或将使用的其他列表达式对象 来推断目标索引或唯一约束。


  • index_where– 可用于推断条件目标索引的附加 WHERE 标准。


方法 sqlalchemy.dialects.sqlite.Insert. on_conflict_do_updateindex_elements:Iterable[Column[Any]strDDLConstraintColumnRole]None=None, index_where:WhereHavingRoleNone=None, set_:Mapping[Any,Any]ColumnCollection[Any,Any]None=None, where:WhereHavingRoleNone=None) Self


为 ON CONFLICT 子句指定 DO UPDATE SET作。


参数

  • index_elements– 由字符串 column names、Column 组成的序列 对象或将使用的其他列表达式对象 来推断目标索引或唯一约束。


  • index_where– 可用于推断条件目标索引的附加 WHERE 标准。

  • set_


    一个字典或其他映射对象,其中键是目标表中的列名,或者是 Column 对象或与目标表匹配的其他 ORM 映射列,以及表达式或文本作为值,指定要执行的 SET作。


    1.4 版本中的新功能: Insert.on_conflict_do_update.set_ 参数支持来自目标的 Column 对象 作为键。


    警告


    此字典考虑 Python 指定的默认 UPDATE 值或生成函数,例如使用 Column.onupdate 指定的值。 对于 ON CONFLICT 样式 UPDATE,除非它们是在 Insert.on_conflict_do_update.set_ 字典。


  • where – 可选参数。表示 WHERE 的表达式对象 限制受 DO UPDATE SET 影响的行的子句。不满足 WHERE 条件的行将不会被更新(实际上是 对这些行执行任何)。


Pysqlite 的¶


通过 pysqlite 驱动程序支持 SQLite 数据库。


请注意,pysqlitesqlite3 的驱动程序相同 模块。


数据库接口


pysqlite 的文档和下载信息(如果适用)可在以下位置获得: https://docs.python.org/library/sqlite3.html


连接


连接字符串:

sqlite+pysqlite:///file_path


驱动


sqlite3 Python DBAPI 是所有现代 Python 版本的标准配置;对于 cPython 和 Pypy,无需额外安装。


连接字符串


SQLite 数据库的文件规范被视为 URL 的“数据库”部分。请注意,SQLAlchemy url 的格式为:

driver://user:pass@host/database


这意味着要使用的实际文件名从第三个斜杠右侧的字符开始。因此,连接到相对文件路径如下所示:

# relative path
e = create_engine("sqlite:///path/to/database.db")


绝对路径(以斜杠开头)表示,意味着需要四个斜杠:

# absolute path
e = create_engine("sqlite:////path/to/database.db")


要使用 Windows 路径,可以使用常规驱动器规范和反斜杠。可能需要双反斜杠:

# absolute path on Windows
e = create_engine("sqlite:///C:\\path\\to\\database.db")


要使用 sqlite :memory: database 使用 sqlite:///:memory:。如果不存在 filepath,它也是默认值,只指定 sqlite:// 而不是其他任何内容:

# in-memory database (note three slashes)
e = create_engine("sqlite:///:memory:")
# also in-memory database
e2 = create_engine("sqlite://")


URI 连接


SQLite 的现代版本支持使用 驱动程序级别 URI,其优点是可以传递其他驱动程序级参数,包括“只读”等选项。Python sqlite3 驱动程序在现代 Python 3 版本下支持此模式。SQLAlchemy pysqlite 驱动程序通过在 URL 查询字符串中指定 “uri=true” 来支持这种使用模式。SQLite 级别的 “URI” 保留为 SQLAlchemy url 的 “database” 部分(即,在斜杠后面):

e = create_engine("sqlite:///file:path/to/database?mode=ro&uri=true")


注意


“uri=true” 参数必须出现在查询字符串中 的 URL。 如果只是 存在于 create_engine.connect_args 中 parameter 字典。


该逻辑通过将属于 Python sqlite3 驱动程序的参数与属于 SQLite URI 的参数分开,来协调 SQLAlchemy 的查询字符串和 SQLite 的查询字符串同时存在。这是通过使用驱动程序的 Python 端已知接受的固定参数列表来实现的。例如,要包含指示 Python sqlite3 “timeout” 和 “check_same_thread” 参数以及 SQLite “mode” 和 “nolock” 参数的 URL,它们都可以在查询字符串上一起传递:

e = create_engine(
    "sqlite:///file:path/to/database?"
    "check_same_thread=true&timeout=10&mode=ro&nolock=1&uri=true"
)


在上面,pysqlite / sqlite3 DBAPI 将按以下方式传递参数:

sqlite3.connect(
    "file:path/to/database?mode=ro&nolock=1",
    check_same_thread=True,
    timeout=10,
    uri=True,
)


关于添加到 Python 或本机驱动程序的未来参数。新增功能 添加到 SQLite URI 方案的参数名称应自动 由该计划容纳。 已添加到 Python 驱动程序的新参数名称 side 可以通过在 create_engine.connect_args 字典中,直到 SQLAlchemy 添加了方言支持。对于本机 SQLite 驱动程序添加与现有的已知 Python 驱动程序参数之一重叠的新参数名称(例如“超时”)的可能性较小的情况,SQLAlchemy 的方言需要调整 URL 方案以继续支持这一点。


与所有 SQLAlchemy 方言一样,整个 “URL” 过程可以在 create_engine() 中通过使用 create_engine.creator 参数,它允许自定义可调用对象 直接创建 Python sqlite3 驱动程序级连接。


在 1.3.9 版本加入.


另请参阅


统一资源标识符 - 在 SQLite 文档中


正则表达式支持


在 1.4 版本加入.


使用 Python 的 re.search 函数提供对 ColumnOperators.regexp_match() 运算符的支持。SQLite 本身不包含有效的正则表达式运算符。相反,它包括一个未实现的占位符运算符 REGEXP,该运算符调用必须提供的用户定义函数。


SQLAlchemy 的实现使用 pysqlite create_function 钩子,如下所示:

def regexp(a, b):
    return re.search(a, b) is not None


sqlite_connection.create_function(
    "regexp",
    2,
    regexp,
)


目前不支持将正则表达式标志作为单独的参数,因为SQLite的REGEXP运算符不支持这些标志,但是这些标志可以内联包含在正则表达式字符串中。有关详细信息,请参阅 Python 正则表达式


另请参阅


Python 正则表达式:Python 正则表达式语法的文档。


与 sqlite3 “原生” 日期和日期时间类型的兼容性


pysqlite 驱动程序包括 sqlite3。PARSE_DECLTYPES 和 sqlite3 的PARSE_COLNAMES选项具有任何显式转换为 “date” 或 “timestamp” 的列或表达式的效果,这些选项将转换为 Python date 或 datetime 对象。pysqlite 方言提供的日期和日期时间类型当前与这些选项不兼容,因为它们呈现 ISO 日期/日期时间,包括微秒,而 pysqlite 的驱动程序则不然。此外,SQLAlchemy 此时不会自动呈现独立函数 “current_timestamp” 和 “current_date” 所需的 “cast” 语法,以在本地返回日期时间/日期类型。不幸的是,pysqlite 没有在 cursor.description 中提供标准的 DBAPI 类型,这使得 SQLAlchemy 无法在没有昂贵的每行类型检查的情况下动态检测这些类型。


请记住,不推荐也不必要使用 pysqlite 的解析选项来与 SQLAlchemy 一起使用,如果在 create_engine() 上配置“native_datetime=True”,则可以强制使用 PARSE_DECLTYPES:

engine = create_engine(
    "sqlite://",
    connect_args={
        "detect_types": sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES
    },
    native_datetime=True,
)


启用此标志后,DATE 和 TIMESTAMP 类型(但请注意 - 不是 DATETIME 或 TIME 类型...困惑了吗?不会执行任何 bind 参数或结果处理。执行 “func.current_date()” 将返回一个字符串。在 SQLAlchemy 中,“func.current_timestamp()” 被注册为返回 DATETIME 类型,因此此函数仍接收 SQLAlchemy 级别的结果处理。


线程/池化行为


默认情况下,sqlite3 DBAPI 禁止在线程中使用特定连接,该连接不是创建它的线程。随着 SQLite 的成熟,它在多线程下的行为得到了改善,甚至包括在多个线程中使用的仅内存数据库的选项。


线程禁止被称为“检查同一线程”,可以使用 sqlite3 参数check_same_thread进行控制,这将禁用或 启用此检查。SQLAlchemy 的默认行为在这里是将 每当基于文件的数据库时,check_same_thread 都会自动设置为 False 正在使用中,以建立与默认池类的兼容性 QueuePool 的 QueuePool 中。


SQLAlchemy pysqlite DBAPI 根据请求的 SQLite 数据库类型以不同的方式建立连接池:


  • 当指定 :memory: SQLite 数据库时,方言默认将使用 SingletonThreadPool。这个池为每个线程维护一个连接,以便当前线程中对引擎的所有访问都使用相同的 :memory: 数据库 - 其他线程将访问不同的 :memory: 数据库。check_same_thread 参数默认为 True


  • 指定基于文件的数据库时,方言将使用 QueuePool 作为连接源。同时,除非被覆盖,否则 check_same_thread 标志默认设置为 False。


    在 2.0 版更改: SQLite 文件数据库引擎现在默认使用 QueuePool。以前,使用的是 NullPoolNullPool 类 可以通过 create_engine.pool类参数


禁用文件数据库的连接池


对于基于文件的数据库,可以通过指定 poolclass()NullPool 实现 参数:

from sqlalchemy import NullPool

engine = create_engine("sqlite:///myfile.db", poolclass=NullPool)


据观察,由于缺乏 QueuePool 实现的连接重用,因此 NullPool 实现在重复检出时产生的性能开销非常小。但是,如果应用程序遇到文件被锁定的问题,则使用此类仍然可能是有益的。


在多个线程中使用内存数据库


要在多线程场景中使用 :memory: 数据库,相同的 Connection 对象必须在线程之间共享,因为数据库存在 仅在该连接的范围内。 这 StaticPool 实现将全局维护单个连接,并且 check_same_thread 标志可以作为 False 传递给 Pysqlite:

from sqlalchemy.pool import StaticPool

engine = create_engine(
    "sqlite://",
    connect_args={"check_same_thread": False},
    poolclass=StaticPool,
)


请注意,在多个线程中使用 :memory: 数据库需要最新版本的 SQLite。


在 SQLite 中使用临时表


由于 SQLite 处理临时表的方式,如果您希望在基于文件的 SQLite 数据库中跨连接池的多个检出使用临时表,例如在使用 ORM 会话时,临时表应继续保留在 Session.commit()Session.rollback() 调用时,必须使用维护单个连接的池。如果范围仅在当前线程中需要,请使用 SingletonThreadPool,或者在这种情况下,多个线程中需要 StaticPool 范围:

# maintain the same connection per thread
from sqlalchemy.pool import SingletonThreadPool

engine = create_engine("sqlite:///mydb.db", poolclass=SingletonThreadPool)


# maintain the same connection across all threads
from sqlalchemy.pool import StaticPool

engine = create_engine("sqlite:///mydb.db", poolclass=StaticPool)


请注意,应为要使用的线程数配置 SingletonThreadPool;超过该数量,连接将以不确定的方式关闭。


处理混合字符串 / 二进制列


SQLite 数据库是弱类型的,因此当使用二进制值时,在 Python 中表示为 b'some string',特定的 SQLite 数据库可以在不同的行中具有数据值,其中一些值将被 Pysqlite 驱动程序作为 b'' 值返回,而其他的将作为 Python 字符串返回, 例如 '' 值。如果使用 SQLAlchemy LargeBinary 数据类型,则不知道是否会出现这种情况 但是,如果特定的 SQLite 数据库具有 直接使用 Pysqlite 驱动程序插入,或者在使用 SQLAlchemy 时插入 String 类型(后来更改为 LargeBinary)时,该 表将无法始终可读,因为 SQLAlchemy 的 LargeBinary 数据类型不处理字符串,因此它无法对字符串格式的值进行“编码”。


要处理在同一列中具有混合字符串/二进制数据的 SQLite 表,请使用将单独检查每一行的自定义类型:

from sqlalchemy import String
from sqlalchemy import TypeDecorator


class MixedBinary(TypeDecorator):
    impl = String
    cache_ok = True

    def process_result_value(self, value, dialect):
        if isinstance(value, str):
            value = bytes(value, "utf-8")
        elif value is not None:
            value = bytes(value)

        return value


然后在 通常会使用 LargeBinary


可序列化隔离 / 保存点 / 事务性 DDL


数据库锁定行为/并发部分中,我们提到了 pysqlite 驱动程序的各种问题,这些问题会阻止 SQLite 的多个功能正常工作。pysqlite DBAPI 驱动程序有几个长期存在的错误,这些错误会影响其事务行为的正确性。在其默认作模式下,SQLite 功能(如 SERIALIZABLE 隔离、事务 DDL 和 SAVEPOINT 支持)是无效的,为了使用这些功能,必须采取解决方法。


问题本质上是驱动程序试图猜测用户的意图,无法启动事务,有时还会过早结束它们,以尽量减少 SQLite 数据库的文件锁定行为,即使 SQLite 本身对只读活动使用“共享”锁。


SQLAlchemy 默认选择不改变此行为,因为这是 pysqlite 驱动程序的长期预期行为;如果 Pysqlite 驱动程序尝试修复这些问题,那将更像是 SQLAlchemy 默认值的驱动力。


好消息是,通过一些事件,我们可以通过完全禁用 pysqlite 的功能并自己发出 BEGIN 来完全实现事务支持。这是使用两个事件侦听器实现的:

from sqlalchemy import create_engine, event

engine = create_engine("sqlite:///myfile.db")


@event.listens_for(engine, "connect")
def do_connect(dbapi_connection, connection_record):
    # disable pysqlite'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, "begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.exec_driver_sql("BEGIN")


警告


使用上述配方时,建议不要使用 Connection.execution_options.isolation_level 设置 连接create_engine() 使用 SQLite 驱动程序, 因为这个函数也必然会改变 “.isolation_level” 设置。


在上面,我们拦截一个新的 pysqlite 连接并禁用任何事务集成。然后,在 SQLAlchemy 知道事务范围要开始时,我们自己发出 “BEGIN”。


当我们控制 “BEGIN” 时,我们也可以直接控制 SQLite 的 锁定模式,介绍于 BEGIN TRANSACTION,通过将所需的锁定模式添加到我们的 “BEGIN” 中:

@event.listens_for(engine, "begin")
def do_begin(conn):
    conn.exec_driver_sql("BEGIN EXCLUSIVE")


另请参阅


BEGIN TRANSACTION - 在 SQLite 站点上


sqlite3 SELECT 不 BEGIN 事务 - 在 Python 错误跟踪器上


sqlite3 模块会破坏事务并可能损坏数据 - 在 Python 错误跟踪器上


用户定义的函数


Pysqlite 支持 create_function() 方法,它允许我们在 Python 中创建自己的用户定义函数 (UDF) 并直接在 SQLite 查询中使用它们。 这些函数使用特定的 DBAPI 连接进行注册。


SQLAlchemy 使用基于文件的 SQLite 数据库的连接池,因此我们需要确保在创建连接时将 UDF 附加到连接上。这是通过事件侦听器完成的:

from sqlalchemy import create_engine
from sqlalchemy import event
from sqlalchemy import text


def udf():
    return "udf-ok"


engine = create_engine("sqlite:///./db_file")


@event.listens_for(engine, "connect")
def connect(conn, rec):
    conn.create_function("udf", 0, udf)


for i in range(5):
    with engine.connect() as conn:
        print(conn.scalar(text("SELECT UDF()")))

Aiosqlite


通过 aiosqlite 驱动程序支持 SQLite 数据库。


数据库接口


aiosqlite 的文档和下载信息(如果适用)可在以下网址获得: https://pypi.org/project/aiosqlite/


连接


连接字符串:

sqlite+aiosqlite:///file_path


aiosqlite 方言为在 pysqlite 上运行的 SQLAlchemy asyncio 接口提供支持。


AIOSklite 是 PySQLite 的包装器,它为每个连接使用后台线程。它实际上并不使用非阻塞 IO,因为 SQLite 数据库不是基于套接字的。但是,它确实提供了一个有效的 asyncio 接口,可用于测试和原型设计目的。


使用特殊的 asyncio 中介层,aiosqlite 方言可用作 SQLAlchemy asyncio 的后端 扩展包。


这种方言通常只能与 create_async_engine() 引擎创建函数:

from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine("sqlite+aiosqlite:///filename")


URL 将所有参数传递给 pysqlite 驱动程序,因此所有连接参数与 Pysqlite 的连接参数相同。


用户定义的函数


aiosqlite 扩展了 pysqlite 以支持异步,因此我们可以在 Python 中创建自己的用户定义函数 (UDF),并直接在 SQLite 查询中使用它们,如下所述:用户定义的函数


可序列化隔离 / 保存点 / 事务性 DDL (asyncio 版本)


与 pysqlite 类似,aiosqlite 不支持 SAVEPOINT 功能。


该解决方案类似于 Serializable isolation / Savepoints / Transactional DDL。这是通过异步事件侦听器实现的:

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")


警告


使用上述配方时,建议不要使用 Connection.execution_options.isolation_level 设置 连接create_engine() 使用 SQLite 驱动程序, 因为这个函数也必然会改变 “.isolation_level” 设置。

Pysqlcipher


通过 pysqlcipher 驱动程序支持 SQLite 数据库。


Dialect 支持使用 SQLCipher 后端。


连接


连接字符串:

sqlite+pysqlcipher://:passphrase@/file_path[?kdf_iter=<iter>]


驱动


当前的 dialect 选择逻辑为:


警告


不再维护 pysqlcipher3pysqlcipher DBAPI 驱动程序;在撰写本文时,SQLCucipher3 驱动程序似乎是最新的。为了将来的兼容性,可以按如下方式使用任何与 pysqlcipher 兼容的 DBAPI:

import sqlcipher_compatible_driver

from sqlalchemy import create_engine

e = create_engine(
    "sqlite+pysqlcipher://:password@/dbname.db",
    module=sqlcipher_compatible_driver,
)


这些驱动程序使用 SQLCipher 引擎。该系统本质上是向 SQLite 引入了新的 PRAGMA 命令,该命令允许设置密码和其他加密参数,从而允许加密数据库文件。


连接字符串


连接字符串的格式在各方面都与 pysqlite 驱动程序的格式相同,只是现在接受“password”字段,它应该包含一个密码:

e = create_engine("sqlite+pysqlcipher://:testing@/foo.db")


对于绝对文件路径,数据库名称应使用两个前导斜杠:

e = create_engine("sqlite+pysqlcipher://:testing@//path/to/foo.db")


SQLCipher 支持的其他加密相关 pragma 的选择(如 https://www.zetetic.net/sqlcipher/sqlcipher-api/ 中所述)可以在查询字符串中传递,并将导致为每个新连接调用该 PRAGMA。目前支持 cipherkdf_itercipher_page_sizecipher_use_hmac

e = create_engine(
    "sqlite+pysqlcipher://:testing@/foo.db?cipher=aes-256-cfb&kdf_iter=64000"
)


警告


以前版本的 sqlalchemy 没有考虑在 url 字符串中传递的与加密相关的 pragmas,这些 pragma 被静默忽略。如果加密选项不匹配,则在打开由以前的 sqlalchemy 版本保存的文件时,这可能会导致错误。


池化行为


驱动程序对 pysqlite 的默认池行为进行了更改,如 线程/池行为中所述。据观察,pysqlcipher 驱动程序在连接时的速度明显慢于 pysqlite 驱动程序,这很可能是由于加密开销,因此此处的方言默认使用 SingletonThreadPool 实现 而不是 pysqlite 使用的 NullPool 池。 与往常一样,泳池 实现完全可以使用 create_engine.pool类参数;这 StaticPool 可能更适合单线程使用,或者 NullPool 可用于防止未加密的连接长时间保持打开状态,但代价是新连接的启动时间较慢。