SQLAlchemy 2.0 文档
方言
- PostgreSQL
- MySQL 和 MariaDB
- SQLite¶
- Oracle
- Microsoft SQL Server
项目版本
- 上一页: MySQL 和 MariaDB
- 下一页: Oracle
- 上一级: 主页
- 本页内容
SQLite¶
对 SQLite 数据库的支持。
下表总结了当前对数据库发布版本的支持级别。
支持类型 |
版本 |
---|---|
3.12+ |
|
3.7.16+ |
DBAPI 支持¶
以下方言/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
,以便该列继续具有文本亲和性。
另请参阅
类型亲和性 - 在 SQLite 文档中
SQLite 自动递增行为¶
有关 SQLite 自动递增的背景信息,请访问:https://sqlite.ac.cn/autoinc.html
关键概念
SQLite 具有一个隐式“自动递增”功能,该功能适用于任何非复合主键列,该列专门使用“INTEGER PRIMARY KEY”创建类型+主键。
SQLite 还具有一个显式的“AUTOINCREMENT”关键字,它不等效于隐式自动递增功能;不建议在一般情况下使用此关键字。除非使用特定于 SQLite 的特殊指令(见下文),否则 SQLAlchemy 不会渲染此关键字。但是,它仍然要求列的类型命名为“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 自动递增功能(无论是隐式还是显式启用)都要求列类型的名称与字符串“INTEGER”完全相同。因此,如果应用程序使用诸如 BigInteger
这样的类型作为主键,则在 SQLite 上,此类型需要在发出初始 CREATE TABLE
语句时渲染为名称 "INTEGER"
,以便自动递增行为可用。
实现此目的的一种方法是在 SQLite 上仅使用 Integer
,使用 TypeEngine.with_variant()
table = Table(
"my_table", metadata,
Column("id", BigInteger().with_variant(Integer, "sqlite"), primary_key=True)
)
另一种方法是使用 BigInteger
的子类,该子类会覆盖其 DDL 名称,使其在编译到 SQLite 时为 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 默认情况下缺乏写入并发的更多信息,请参阅 Situations Where Another RDBMS May Work Better - High Concurrency 页面底部。
以下小节介绍受 SQLite 文件系统架构影响的区域,此外,在使用 pysqlite 驱动程序时,这些区域通常需要变通方法才能正常工作。
事务隔离级别 / 自动提交¶
SQLite 以非标准方式支持“事务隔离”,有两个方面。一个是 PRAGMA read_uncommitted 指令。此设置可以实质上将 SQLite 在其默认的 SERIALIZABLE
隔离模式和通常称为 READ UNCOMMITTED
的“脏读”隔离模式之间切换。
SQLAlchemy 通过 create_engine.isolation_level
参数连接到此 PRAGMA 语句 create_engine()
。在与 SQLite 一起使用时,此参数的有效值为 "SERIALIZABLE"
和 "READ UNCOMMITTED"
,分别对应于值 0 和 1。SQLite 默认使用 SERIALIZABLE
,但其行为会受到 pysqlite 驱动程序默认行为的影响。
使用 pysqlite 驱动程序时,还可以使用 "AUTOCOMMIT"
隔离级别,它将使用 DBAPI 连接上的 .isolation_level
属性更改 pysqlite 连接,并在设置持续期间将其设置为 None。
新版本 1.3.16 中添加: 在使用 pysqlite / sqlite3 SQLite 驱动程序时,添加了对 SQLite AUTOCOMMIT 隔离级别的支持。
影响 SQLite 事务锁定的另一个方面是使用的 BEGIN
语句的性质。三种类型是“deferred”、“immediate”和“exclusive”,如 BEGIN TRANSACTION 所述。一个简单的 BEGIN
语句使用“deferred”模式,其中数据库文件在第一次读或写操作之前不会被锁定,并且读访问权在第一次写操作之前对其他事务保持开放。但同样,需要注意的是,pysqlite 驱动程序会通过直到第一次写操作才发出 BEGIN的方式干预此行为。
警告
SQLite 的事务范围受到 pysqlite 驱动程序中未解决的问题的影响,该驱动程序将 BEGIN 语句延迟的程度远远超过通常可行的情况。请参阅部分 Serializable isolation / Savepoints / Transactional DDL 或 Serializable isolation / Savepoints / Transactional DDL (asyncio version),了解解决此行为的技巧。
INSERT/UPDATE/DELETE…RETURNING¶
SQLite 方言支持 SQLite 3.35 的 INSERT|UPDATE|DELETE..RETURNING
语法。在某些情况下,可以自动使用 INSERT..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 语句延迟的程度远远超过通常可行的情况。请参阅部分 Serializable isolation / Savepoints / Transactional DDL 和 Serializable isolation / Savepoints / Transactional DDL (asyncio version),了解解决此行为的技巧。
事务性 DDL¶
SQLite 数据库也支持事务性 DDL。在这种情况下,pysqlite 驱动程序不仅无法启动事务,而且还会在检测到 DDL 时结束任何现有事务,因此也需要变通方法。
警告
SQLite 的事务性 DDL 受 pysqlite 驱动程序中未解决的问题的影响,该驱动程序无法发出 BEGIN,此外,在遇到 DDL 时,它还会强制执行 COMMIT 以取消任何事务。请参阅部分 Serializable isolation / Savepoints / Transactional DDL,了解解决此行为的技巧。
外键支持¶
SQLite 在为表发出 CREATE 语句时支持 FOREIGN KEY 语法,但是默认情况下,这些约束对表的运行没有影响。
SQLite 上的约束检查有三个先决条件
必须使用至少 3.6.19 版本的 SQLite。
SQLite 库必须不启用 SQLITE_OMIT_FOREIGN_KEY 或 SQLITE_OMIT_TRIGGER 符号进行编译。
必须在使用之前(包括对
MetaData.create_all()
的初始调用)在所有连接上发出PRAGMA foreign_keys = ON
语句。
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 外键时,无法为包含相互依赖的外键约束的表发出 CREATE 或 DROP 语句;要为这些表发出 DDL,需要使用 ALTER TABLE 分别创建或删除这些约束,SQLite 不支持此功能。
另请参阅
SQLite Foreign Key Support - 在 SQLite 网站上。
Events - SQLAlchemy 事件 API。
- Creating/Dropping Foreign Key Constraints via ALTER - 有关 SQLAlchemy 处理的更多信息
相互依赖的外键约束。
约束的 ON CONFLICT 支持¶
另请参阅
本节介绍 SQLite 的“ON CONFLICT”的 DDL 版本,它出现在 CREATE TABLE 语句中。有关应用于 INSERT 语句的“ON CONFLICT”,请参阅 INSERT…ON CONFLICT (Upsert)。
SQLite 支持一种非标准的 DDL 子句,称为 ON CONFLICT,它可以应用于主键、唯一、检查和非空约束。在 DDL 中,它根据目标约束的位置在“CONSTRAINT”子句内或列定义内呈现。要在 DDL 内呈现此子句,可以将扩展参数 sqlite_on_conflict
与 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
)
要为 NOT NULL 约束应用 FAIL 算法,请使用 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
)
INSERT…ON CONFLICT(Upsert)¶
另请参阅
本节描述 SQLite 的 DML 版“ON CONFLICT”,它发生在 INSERT 语句中。有关应用于 CREATE TABLE 语句的“ON CONFLICT”,请参见 ON CONFLICT 对约束的支持。
从 3.24.0 版开始,SQLite 支持通过 INSERT
语句的 ON CONFLICT
子句对表中的行执行“upsert”(更新或插入)。仅当该行不违反任何唯一或主键约束时才会插入候选行。如果发生唯一约束违反,则会发生次要操作,该操作可以是“DO UPDATE”,表示应更新目标行中的数据,也可以是“DO NOTHING”,表示静默跳过此行。
使用构成现有唯一约束和索引的列来确定冲突。通过声明构成索引的列和条件来识别这些约束。
SQLAlchemy 通过特定于 SQLite 的 insert()
函数提供 ON CONFLICT
支持,该函数提供生成方法 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 表达式元素的序列,这些元素将标识唯一索引或唯一约束。使用
Insert.on_conflict_do_update.index_elements
推断索引时,可以通过同时指定Insert.on_conflict_do_update.index_where
参数来推断部分索引。>>> stmt = insert(my_table).values(user_email='[email protected]', 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 子句¶
ON CONFLICT...DO UPDATE
用于使用任何组合的新值以及来自建议插入的值来执行对已存在行的更新。这些值使用 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
指定的函数)。这些值不会为 ON CONFLICT 类型的 UPDATE 执行,除非它们在 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
参数的 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 方言对于在查找映射中找不到特定类型名称时具有不同的“回退”例程;它改为实现位于 https://www.sqlite.org/datatype3.html 第 2.1 节中的 SQLite “类型亲和性”方案。
提供的类型映射将根据以下类型的精确字符串名称匹配进行直接关联。
BIGINT
,BLOB
,BOOLEAN
,BOOLEAN
,CHAR
,DATE
,DATETIME
,FLOAT
,DECIMAL
,FLOAT
,INTEGER
,INTEGER
,NUMERIC
,REAL
,SMALLINT
,TEXT
,TIME
,TIMESTAMP
,VARCHAR
,NVARCHAR
,NCHAR
当类型名称与上述类型不匹配时,将使用“类型亲和力”查找。
部分索引¶
可以使用 DDL 系统使用参数 sqlite_where
指定部分索引,例如使用 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 驱动程序在 SQLite 的 3.10.0 版本之前存在一个错误,该错误要求 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
基础
engine = create_engine("sqlite://", execution_options={"sqlite_raw_colnames": True})
使用每个 Engine
执行选项时,请注意,使用 UNION 的 Core 和 ORM 查询可能无法正常工作。
特定于 SQLite 的表选项¶
SQLite 方言与 Table
构造一起直接支持 CREATE TABLE 的一个选项。
WITHOUT ROWID
:Table("some_table", metadata, ..., sqlite_with_rowid=False)
反映内部模式表¶
返回表列表的反射方法将省略所谓的“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,
)
对象名称 | 描述 |
---|---|
使用字符串在 SQLite 中表示 Python 日期对象。 |
|
使用字符串在 SQLite 中表示 Python 日期时间对象。 |
|
SQLite JSON 类型。 |
|
使用字符串在 SQLite 中表示 Python 时间对象。 |
- class sqlalchemy.dialects.sqlite.DATETIME¶
使用字符串在 SQLite 中表示 Python 日期时间对象。
默认的字符串存储格式为
"%(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_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+)" )
- 参数::
类签名
class
sqlalchemy.dialects.sqlite.DATETIME
(sqlalchemy.dialects.sqlite.base._DateTimeMixin
,sqlalchemy.types.DateTime
)
- class sqlalchemy.dialects.sqlite.DATE¶
使用字符串在 SQLite 中表示 Python 日期对象。
默认的字符串存储格式为
"%(year)04d-%(month)02d-%(day)02d"
例如
2011-03-15
默认情况下,传入的存储格式使用 Python
date.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+)") )
- 参数::
类签名
class
sqlalchemy.dialects.sqlite.DATE
(sqlalchemy.dialects.sqlite.base._DateTimeMixin
,sqlalchemy.types.Date
)
- class sqlalchemy.dialects.sqlite.JSON¶
SQLite JSON 类型。
SQLite 从版本 3.9 开始通过其 JSON1 扩展支持 JSON。请注意,JSON1 是一个 可加载扩展,因此可能不可用,或者可能需要在运行时加载。
JSON
在基础JSON
数据类型用于 SQLite 后端时会自动使用。另请参阅
JSON
- 通用跨平台 JSON 数据类型的基本文档。JSON
类型支持将 JSON 值持久化以及JSON
数据类型提供的核心索引操作,方法是通过调整操作以在数据库级别呈现包装在JSON_QUOTE
函数中的JSON_EXTRACT
函数。提取的值被引用以确保结果始终是 JSON 字符串值。新版本 1.3 中添加。
成员
类签名
class
sqlalchemy.dialects.sqlite.JSON
(sqlalchemy.types.JSON
)-
method
sqlalchemy.dialects.sqlite.JSON.
__init__(none_as_null: bool = False)¶ 从
sqlalchemy.types.JSON.__init__
方法继承JSON
构造一个
JSON
类型。- 参数::
none_as_null=False¶ –
如果为 True,将值
None
持久化为 SQL NULL 值,而不是null
的 JSON 编码。请注意,当此标志为 False 时,null()
结构仍然可以用于持久化 NULL 值,它可以作为由JSON
类型特殊解释的直接参数值传递,作为 SQL NULLfrom sqlalchemy import null conn.execute(table.insert(), {"data": null()})
注意
JSON.none_as_null
不适用于传递给Column.default
和Column.server_default
的值;传递给这些参数的None
值表示“没有默认值”。此外,在 SQL 比较表达式中使用时,Python 值
None
继续引用 SQL null,而不是 JSON NULL。JSON.none_as_null
标志明确地引用 INSERT 或 UPDATE 语句中值的 **持久性**。JSON.NULL
值应用于希望与 JSON null 进行比较的 SQL 表达式。另请参阅
-
method
- class sqlalchemy.dialects.sqlite.TIME¶
使用字符串在 SQLite 中表示 Python 时间对象。
默认的字符串存储格式为
"%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
例如
12:05:57.10558
默认情况下,传入的存储格式使用 Python
time.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+))?") )
- 参数::
类签名
class
sqlalchemy.dialects.sqlite.TIME
(sqlalchemy.dialects.sqlite.base._DateTimeMixin
,sqlalchemy.types.Time
)
SQLite DML 结构¶
对象名称 | 描述 |
---|---|
insert(table) |
构建一个 SQLite 特定的变体 |
SQLite 特定的 INSERT 实现。 |
- function sqlalchemy.dialects.sqlite.insert(table: _DMLTableArgument) → Insert¶
构建一个 SQLite 特定的变体
Insert
结构。该
sqlalchemy.dialects.sqlite.insert()
函数创建一个sqlalchemy.dialects.sqlite.Insert
。此类基于与方言无关的Insert
结构,可以使用insert()
函数在 SQLAlchemy Core 中构建。该
Insert
结构包含附加方法Insert.on_conflict_do_update()
、Insert.on_conflict_do_nothing()
。
- class sqlalchemy.dialects.sqlite.Insert¶
SQLite 特定的 INSERT 实现。
添加 SQLite 特定语法的方法,例如 ON CONFLICT。
该
Insert
对象使用sqlalchemy.dialects.sqlite.insert()
函数创建。1.4 版中的新增功能。
-
attribute
sqlalchemy.dialects.sqlite.Insert.
excluded¶ 为 ON CONFLICT 语句提供
excluded
命名空间SQLite 的 ON CONFLICT 子句允许引用要插入的行,称为
excluded
。此属性提供此行中的所有列以供引用。提示
该
Insert.excluded
属性是ColumnCollection
的实例,它提供的接口与Table.c
集合在 访问表和列 中描述的接口相同。使用此集合,普通名称可以像属性一样访问(例如stmt.excluded.some_column
),但特殊名称和字典方法名称应使用索引访问,例如stmt.excluded["column name"]
或stmt.excluded["values"]
。有关更多示例,请参阅ColumnCollection
的文档字符串。
-
attribute
sqlalchemy.dialects.sqlite.Insert.
inherit_cache: bool | None = False¶ 指示此
HasCacheKey
实例是否应使用其直接超类使用的缓存键生成方案。此属性默认为
None
,表示构造尚未考虑其是否适合参与缓存;这在功能上等同于将该值设置为False
,除了还会发出警告。如果与对象对应的 SQL 不会根据此类特定的属性(而不是其超类)而更改,则可以将此标志设置为
True
。另请参阅
启用自定义构造的缓存支持 - 为第三方或用户定义的 SQL 构造设置
HasCacheKey.inherit_cache
属性的一般指南。
-
method
sqlalchemy.dialects.sqlite.Insert.
on_conflict_do_nothing(index_elements: _OnConflictIndexElementsT = None, index_where: _OnConflictIndexWhereT = None) → Self¶ 为 ON CONFLICT 子句指定 DO NOTHING 操作。
-
method
sqlalchemy.dialects.sqlite.Insert.
on_conflict_do_update(index_elements: _OnConflictIndexElementsT = None, index_where: _OnConflictIndexWhereT = None, set_: _OnConflictSetT = None, where: _OnConflictWhereT = None) → Self¶ 指定 ON CONFLICT 子句的 DO UPDATE SET 操作。
- 参数::
index_elements¶ – 一个序列,包含字符串列名,
Column
对象,或其他列表达式对象,用于推断目标索引或唯一约束。index_where¶ – 可用于推断条件目标索引的附加 WHERE 条件。
set_¶ –
一个字典或其他映射对象,其中键是目标表中列的名称,或
Column
对象或其他 ORM 映射列,与目标表匹配,值是表达式或字面量,指定要执行的SET
操作。新版功能,版本 1.4:
Insert.on_conflict_do_update.set_
参数支持来自目标Table
的Column
对象作为键。警告
此字典不会考虑 Python 指定的默认 UPDATE 值或生成函数,例如使用
Column.onupdate
指定的函数。这些值不会在 ON CONFLICT 样式的 UPDATE 中使用,除非它们在Insert.on_conflict_do_update.set_
字典中手动指定。where¶ – 可选参数。如果存在,可以是字面量 SQL 字符串或
WHERE
子句的有效表达式,用于限制受DO UPDATE SET
影响的行。不满足WHERE
条件的行将不会更新(对于这些行,实际上是DO NOTHING
)。
-
attribute
Pysqlite¶
通过 pysqlite 驱动程序支持 SQLite 数据库。
请注意,pysqlite
与 Python 分发中包含的 sqlite3
模块相同。
DBAPI¶
pysqlite 的文档和下载信息(如果适用)可在以下位置找到:https://docs.pythonlang.cn/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:
数据库,请使用 sqlite:///:memory:
将其指定为文件名。如果没有文件路径,仅指定 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 的“数据库”部分保留(即,在斜杠之后)
e = create_engine("sqlite:///file:path/to/database?mode=ro&uri=true")
注意
“uri=true”参数必须出现在 URL 的查询字符串中。如果它只存在于 create_engine.connect_args
参数字典中,则当前不会按预期工作。
该逻辑通过分离属于 Python sqlite3 驱动程序的参数和属于 SQLite URI 的参数,来协调 SQLAlchemy 查询字符串和 SQLite 查询字符串同时存在的问题。这是通过使用已知被 Python 驱动程序一侧接受的固定参数列表来实现的。例如,要包含一个 URL,该 URL 指示 Python sqlite3 的“timeout”和“check_same_thread”参数,以及 SQLite 的“mode”和“nolock”参数,它们都可以一起传递到查询字符串上
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 驱动程序一侧的新参数名称可以通过在 create_engine.connect_args
字典中指定它们来适应,直到 SQLAlchemy 添加方言支持。对于不太可能出现的情况,即本机 SQLite 驱动程序添加了一个与现有已知 Python 驱动程序参数(例如,可能为“timeout”)重叠的新参数名称,SQLAlchemy 的方言将需要调整 URL 方案以继续支持此方案。
与所有 SQLAlchemy 方言一样,在 create_engine()
中可以使用 create_engine.creator
参数绕过整个“URL”过程,该参数允许使用自定义可调用对象,该对象直接创建 Python sqlite3 驱动程序级连接。
新版功能,版本 1.3.9。
另请参阅
统一资源标识符 - 在 SQLite 文档中
正则表达式支持¶
1.4 版中的新增功能。
对 ColumnOperators.regexp_match()
运算符的支持是使用 Python 的 re.search 函数提供的。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 日期或时间对象。pysqlite 方言提供的日期和时间类型目前与这些选项不兼容,因为它们呈现包含微秒的 ISO 日期/时间,而 pysqlite 的驱动程序不支持。此外,SQLAlchemy 目前不会自动呈现独立函数“current_timestamp”和“current_date”所需的“cast”语法,以便它们原生返回 datetime/date 类型。不幸的是,pysqlite 不会在 cursor.description
中提供标准 DBAPI 类型,这使得 SQLAlchemy 无法在没有昂贵 per-row 类型检查的情况下动态检测这些类型。
请记住,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 类型……困惑了吗?)将不会执行任何绑定参数或结果处理。“func.current_date()”的执行将返回一个字符串。“func.current_timestamp()”在 SQLAlchemy 中注册为返回 DATETIME 类型,因此此函数仍会接收 SQLAlchemy 级的结果处理。
线程/池行为¶
默认情况下,sqlite3
DBAPI 禁止在不是创建它的线程中使用特定连接。随着 SQLite 的成熟,它在多线程下的行为有所改善,甚至包括内存中数据库在多线程中使用的选项。
线程禁止称为“检查同一线程”,可以使用 sqlite3
参数 check_same_thread
来控制,该参数将禁用或启用此检查。SQLAlchemy 在此处的默认行为是在使用基于文件的数据库时自动将 check_same_thread
设置为 False
,以与默认池类 QueuePool
建立兼容性。
SQLAlchemy 的 pysqlite
DBAPI 根据请求的 SQLite 数据库类型以不同的方式建立连接池
当指定
:memory:
SQLite 数据库时,方言默认将使用SingletonThreadPool
。此池为每个线程维护一个连接,以便当前线程内的所有引擎访问都使用相同的:memory:
数据库 - 其他线程将访问不同的:memory:
数据库。check_same_thread
参数默认设置为True
。当指定基于文件的数据库时,方言将使用
QueuePool
作为连接的来源。同时,check_same_thread
标志默认设置为 False,除非覆盖。更改版本 2.0: SQLite 文件数据库引擎现在默认使用
QueuePool
。以前,使用NullPool
。通过使用create_engine.poolclass
参数指定它可以使用NullPool
类。
禁用基于文件数据库的连接池¶
可以通过为 poolclass()
参数指定 NullPool
实现来禁用基于文件的数据库的池。
from sqlalchemy import NullPool
engine = create_engine("sqlite:///myfile.db", poolclass=NullPool)
据观察,由于缺少 QueuePool
实现的连接重用,NullPool
实现对重复签出产生了极小的性能开销。但是,如果应用程序遇到文件锁定问题,使用此类仍然可能会有益。
在多个线程中使用内存数据库¶
要在多线程场景中使用 :memory:
数据库,同一个连接对象必须在各个线程之间共享,因为数据库只存在于该连接的范围内。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
时,临时表应该在调用 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
然后使用上面的 MixedBinary
数据类型替换通常使用 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
设置上使用 Connection
和 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 does not BEGIN a transaction - 在 Python 错误跟踪器上
sqlite3 module breaks transactions and potentially corrupts data - 在 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 数据库。
DBAPI¶
aiosqlite 的文档和下载信息(如果有)可在以下网站找到:https://pypi.ac.cn/project/aiosqlite/
连接¶
连接字符串
sqlite+aiosqlite:///file_path
aiosqlite 方言为在 pysqlite 之上运行的 SQLAlchemy asyncio 接口提供支持。
aiosqlite 是 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 功能。
解决方案与 可序列化隔离 / 保存点 / 事务性 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
设置上使用 Connection
和 create_engine()
,以及 SQLite 驱动程序,因为此函数必然还会更改“.isolation_level”设置。
Pysqlcipher¶
通过 pysqlcipher 驱动程序支持 SQLite 数据库。
用于支持使用 SQLCipher 后端的 DBAPI 的方言。
连接¶
连接字符串
sqlite+pysqlcipher://:passphrase@/file_path[?kdf_iter=<iter>]
驱动程序¶
当前的方言选择逻辑为
如果
create_engine.module
参数提供了一个 DBAPI 模块,则使用该模块。否则,对于 Python 3,选择 https://pypi.ac.cn/project/sqlcipher3/
如果不可用,则回退到 https://pypi.ac.cn/project/pysqlcipher3/
对于 Python 2,使用 https://pypi.ac.cn/project/pysqlcipher/。
警告
pysqlcipher3
和 pysqlcipher
DBAPI 驱动程序不再维护;截至目前,sqlcipher3
驱动程序似乎是当前版本。为了将来兼容,可以使用任何与 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 在 https://www.zetetic.net/sqlcipher/sqlcipher-api/ 中记录的各种其他与加密相关的 pragma,这将导致对每个新连接调用该 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 字符串中传递的与加密相关的 pragma,这些 pragma 被静默忽略。如果加密选项不匹配,这可能会导致打开以前版本的 sqlalchemy 保存的文件时出现错误。
池行为¶
驱动程序对 pysqlite 的默认池行为进行了更改,如 线程/池行为 中所述。已观察到 pysqlcipher 驱动程序的连接速度比 pysqlite 驱动程序慢得多,这很可能是由于加密开销导致的,因此这里方言默认使用 SingletonThreadPool
实现,而不是 pysqlite 使用的 NullPool
池。与往常一样,池实现完全可以使用 create_engine.poolclass
参数进行配置; StaticPool
可能更适合单线程使用,或者可以使用 NullPool
来防止未加密的连接长时间保持打开状态,以牺牲新的连接启动时间为代价。
flambé! 龙和炼金术士图像设计由 Rotem Yaari 创建并慷慨捐赠。
使用 Sphinx 7.2.6 创建。文档最后生成时间:2024 年 11 月 8 日星期五 上午 08:41:19 EST