SQLite¶
支持 SQLite 数据库。
下表总结了数据库发行版的当前支持级别。
|
|
---|---|
3.12+ |
|
3.7.16+ |
DBAPI 支持¶
以下 dialect/DBAPI 选项可用。有关连接信息,请参阅各个 DBAPI 部分。
日期和时间类型¶
SQLite 没有内置的 DATE、TIME 或 DATETIME 类型,而 pysqlite 有
不提供在 Python 之间转换值的开箱即用功能
datetime 对象和 SQLite 支持的格式。SQLAlchemy 自己的
DateTime
和相关类型提供日期格式
以及使用 SQLite 时的解析功能。实现类包括
DATETIME、
DATE
和 TIME
。这些类型将日期和时间表示为 ISO 格式的字符串,这也很好地支持排序。这些函数不依赖于典型的 “libc” 内部结构,因此完全支持历史日期。
确保文本亲和性¶
为这些类型渲染的 DDL 是标准的 DATE
、TIME
和 DATETIME
指示器。但是,自定义存储格式也可以应用于这些类型。当检测到存储格式不包含字母字符时,这些类型的 DDL 将呈现为 DATE_CHAR
、TIME_CHAR
和 DATETIME_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 算法在
PrimaryKeyConstraint
、 UniqueConstraint
、
CheckConstraint
对象。在 Column
对象中,有单独的参数sqlite_on_conflict_not_null
sqlite_on_conflict_primary_key
,sqlite_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支持通过INSERT
的ON 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” 查找:
部分索引¶
部分索引,例如使用 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,
)
对象名称 |
描述 |
---|---|
|
|
|
|
|
|
|
-
类 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
默认情况下,使用 Pythondatetime.fromisoformat()
函数解析传入的存储格式。
在 2.0 版本发生变更:datetime.fromisoformat()
用于默认的日期时间字符串解析。
存储格式可以在一定程度上使用storage_format
和regexp
参数,例如: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+)", )
参数
类签名
类sqlalchemy.dialects.sqlite.DATETIME
(sqlalchemy.dialects.sqlite.base._DateTimeMixin
,sqlalchemy.types.DateTime
)
-
类 sqlalchemy.dialects.sqlite。日期¶
使用字符串在 SQLite 中表示 Python 日期对象。
默认字符串存储格式为:"%(year)04d-%(month)02d-%(day)02d"
例如:2011-03-15
默认情况下,使用 Pythondate.fromisoformat()
函数解析传入的存储格式。
在 2.0 版本发生变更:date.fromisoformat()
用于默认日期字符串解析。
存储格式可以在一定程度上使用storage_format
和regexp
参数,例如: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+)"), )
参数
类签名
类sqlalchemy.dialects.sqlite.DATE
(sqlalchemy.dialects.sqlite.base._DateTimeMixin
,sqlalchemy.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.default
和Column.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
默认情况下,使用 Pythontime.fromisoformat()
函数解析传入的存储格式。
在 2.0 版本发生变更:time.fromisoformat()
用于默认时间字符串解析。
存储格式可以在一定程度上使用storage_format
和regexp
参数,例如: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+))?"), )
参数
类签名
类sqlalchemy.dialects.sqlite.TIME
(sqlalchemy.dialects.sqlite.base._DateTimeMixin
,sqlalchemy.types.Time
)
SQLite DML 结构¶
对象名称 |
描述 |
---|---|
|
|
|
-
函数 sqlalchemy.dialects.sqlite。insert(table: _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_nothing(index_elements:Iterable[Column[Any]strDDLConstraintColumnRole]None=None, index_where:WhereHavingRoleNone=None)Self ¶
为 ON CONFLICT 子句指定 DO NOTHING作。
-
方法sqlalchemy.dialects.sqlite.Insert.
on_conflict_do_update(index_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 数据库。
请注意,pysqlite
与 sqlite3
的驱动程序相同
模块。
数据库接口¶
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
。以前,使用的是NullPool
。NullPool
类 可以通过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 选择逻辑为:
如果create_engine.module
参数提供 DBAPI 模块,则使用该模块。
否则,对于 Python 3,请选择 https://pypi.org/project/sqlcipher3/
如果不可用,请回退到 https://pypi.org/project/pysqlcipher3/
对于 Python 2,使用 https://pypi.org/project/pysqlcipher/。
警告
不再维护 pysqlcipher3
和 pysqlcipher
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。目前支持 cipher
、kdf_iter
cipher_page_size
和 cipher_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
可用于防止未加密的连接长时间保持打开状态,但代价是新连接的启动时间较慢。