版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

SQLite的¶ T0>

支持SQLite数据库。

DBAPI支持

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

日期和时间类型

SQLite没有内置的DATE,TIME或DATETIME类型,并且pysqlite不提供在Python datetime对象和SQLite支持的格式之间转换值的开箱即用功能。当使用SQlite时,SQLAlchemy自己的DateTime和相关类型提供了日期格式和分析功能。实现类是DATETIMEDATETIME这些类型表示日期和时间作为ISO格式的字符串,这也很好地支持排序。这些函数没有依赖于典型的“libc”内部,所以完全支持历史日期。

确保文字亲和力

为这些类型提供的DDL是标准的DATETIMEDATETIME指标。但是,自定义存储格式也可以应用于这些类型。当检测到存储格式不包含字母字符时,这些类型的DDL将呈现为DATE_CHARTIME_CHARDATETIME_CHAR该列继续具有文本的亲和力。

也可以看看

Type Affinity - in the SQLite documentation

SQLite自动递增行为

SQLite自动增量的背景是:http://sqlite.org/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视为“整数”关联。However, the SQLite autoincrement feature, whether implicitly or explicitly enabled, requires that the name of the column’s type is exactly the string “INTEGER”. Therefore, if an application uses a type like BigInteger for a primary key, on SQLite this type will need to be rendered as the name "INTEGER" when emitting the initial CREATE TABLE statement in order for the autoincrement behavior to be available.

一种方法是在SQLite上只使用TypeEngine.with_variant()来使用Integer

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

另一个是在使用SQLite编译时,使用BigInteger的子类覆盖其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驱动程序中都有几个因素,这些因素显着地放宽了这个限制。

然而,无论使用什么锁定模式,一旦事务开始,SQLite仍将始终锁定数据库文件,并且DML(例如,INSERT,UPDATE,DELETE)至少已被发出,并且至少在此点他们也试图发射DML。默认情况下,在该块超时之前,该块的时间长度非常短,并且出现错误。

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

有关SQLite缺乏设计写入并发性的更多信息,请参阅另一个RDBMS可能工作得更好的情况 - 接近页面底部的高并发性

以下小节介绍受SQLite基于文件的体系结构影响的区域,另外通常需要在使用pysqlite驱动程序时使用变通方法。

事务隔离级别

SQLite支持两个轴以非标准方式“事务隔离”。One is that of the PRAGMA read_uncommitted instruction. This setting can essentially switch SQLite between its default mode of SERIALIZABLE isolation, and a “dirty read” isolation mode normally referred to as READ UNCOMMITTED.

SQLAlchemy使用create_engine()create_engine.isolation_level参数绑定到此PRAGMA语句中。与SQLite一起使用时,此参数的有效值分别为"SERIALIZABLE"“READ UNCOMMITTED”分别为0和1。SQLite默认为SERIALIZABLE,但是它的行为受到pysqlite驱动默认行为的影响。

The other axis along which SQLite’s transactional locking is impacted is via the nature of the BEGIN statement used. The three varieties are “deferred”, “immediate”, and “exclusive”, as described at BEGIN TRANSACTION. A straight BEGIN statement uses the “deferred” mode, where the the database file is not locked until the first read or write operation, and read access remains open to other transactions until the first write operation. 但同样重要的是要注意,在第一次写操作之前,pysqlite驱动程序通过不会发出BEGIN来干扰这种行为。

警告

SQLite的事务范围会受到pysqlite驱动程序中未解决的问题的影响,因为它将BEGIN语句的推迟程度比往往可行。请参阅Serializable isolation / Savepoints / Transactional DDL一节以解决此问题。

SAVEPOINT支持

SQLite支持SAVEPOINT,它只在事务开始时才起作用。SQLAlchemy的SAVEPOINT支持可以在核心级别使用Connection.begin_nested()方法,在ORM级别使用Session.begin_nested()但是,除非采取解决方法,否则SAVEPOINT将不会与pysqlite一起工作。

警告

SQLite的SAVEPOINT功能会受到pysqlite驱动程序中未解决的问题的影响,这会将BEGIN语句延迟到比通常可行的程度更大的程度。请参阅Serializable isolation / Savepoints / Transactional DDL一节以解决此问题。

事务性DDL

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

警告

SQLite的事务性DDL受到pysqlite驱动程序中未解决的问题的影响,该驱动程序无法发出BEGIN,并且在遇到DDL时强制COMMIT取消任何事务。请参阅Serializable isolation / Savepoints / Transactional DDL一节以解决此问题。

外键支持

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

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

  • SQLite至少必须使用3.6.19版本
  • SQLite库必须在没有启用SQLITE_OMIT_FOREIGN_KEY或SQLITE_OMIT_TRIGGER的情况下编译
  • 在使用之前,必须在所有连接上发射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 - on the SQLite web site.

Events - SQLAlchemy event API.

Creating/Dropping Foreign Key Constraints via ALTER - 关于SQLAlchemy的处理设施的更多信息
相互依赖的外键约束。

类型反射

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

在检查类型时,SQLAlchemy的反射过程使用一个简单的查找表来链接返回给提供的SQLAlchemy类型的关键字。这个查询表在SQLite方言中就像所有其他方言一样。但是,当特定类型名称不在查找映射中时,SQLite方言具有不同的“后退”例程;它实现了位于http://www.sqlite.org/datatype3.html 2.1节的SQLite“type affinity”方案。

提供的类型映射将从以下类型的精确字符串名称匹配中直接关联:

BIGINT, BLOB, BOOLEAN, BOOLEAN, CHAR, DATE, DATETIME, FLOAT, DECIMAL, FLOAT, INTEGER, INTEGER, NUMERIC, REAL, SMALLINT, TEXT, TIME, TIMESTAMP, VARCHAR, NVARCHAR, NCHAR

当类型名称与以上类型之一不匹配时,将使用“类型关联性”查找:

  • INTEGER is returned if the type name includes the string INT
  • TEXT is returned if the type name includes the string CHAR, CLOB or TEXT
  • NullType is returned if the type name includes the string BLOB
  • 如果类型名称包含字符串REALFLOADOUB,则返回REAL
  • 否则,使用NUMERIC类型。

New in version 0.9.3: Support for SQLite type affinity rules when reflecting columns.

部分索引

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

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

New in version 0.9.9.

虚线列名称

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

在版本1.1中更改:从SQLite版本3.10.0开始,下列SQLite问题已得到解决。SQLAlchemy从1.1自动禁用基于此版本检测的内部解决方法。

这个错误,完全在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.execute("create table x (a integer, b integer)")
conn.execute("insert into x (a, b) values (1, 1)")
conn.execute("insert into x (a, b) values (2, 2)")

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

result = conn.execute('''
    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

Therefore, the workaround applied by SQLAlchemy only impacts ResultProxy.keys() and RowProxy.keys() in the public API. In the very specific case where an application is forced to use column names that contain dots, and the functionality of ResultProxy.keys() and RowProxy.keys() is required to return these dotted names unmodified, the sqlite_raw_colnames execution option may be provided, either on a per-Connection basis:

result = conn.execution_options(sqlite_raw_colnames=True).execute('''
    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})

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

SQLite数据类型

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

from sqlalchemy.dialects.sqlite import \
            BLOB, BOOLEAN, CHAR, DATE, DATETIME, DECIMAL, FLOAT, \
            INTEGER, NUMERIC, SMALLINT, TEXT, TIME, TIMESTAMP, \
            VARCHAR
class sqlalchemy.dialects.sqlite.DATETIME(*args, **kwargs)

基础:sqlalchemy.dialects.sqlite.base._DateTimeMixinsqlalchemy.types.DateTime

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

默认的字符串存储格式是:

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

例如。:

2011-03-15 12:05:57.10558

存储格式可以使用storage_formatregexp参数进行一定程度的自定义,例如:

import re
from sqlalchemy.dialects.sqlite import DATETIME

dt = DATETIME(
    storage_format="%(year)04d/%(month)02d/%(day)02d %(hour)02d:%(min)02d:%(second)02d",
    regexp=r"(\d+)/(\d+)/(\d+) (\d+)-(\d+)-(\d+)"
)
参数:
  • storage_format – format string which will be applied to the dict with keys year, month, day, hour, minute, second, and microsecond.
  • regexp – regular expression which will be applied to incoming result rows. 如果正则表达式包含命名组,那么结果匹配字典作为关键字参数应用于Python datetime()构造函数。否则,如果使用位置组,则使用位置参数通过* map(int, match_obj.groups(0))调用datetime T0>。
class sqlalchemy.dialects.sqlite。 DATE storage_format =无 regexp = None** kw

基础:sqlalchemy.dialects.sqlite.base._DateTimeMixinsqlalchemy.types.Date

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

默认的字符串存储格式是:

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

例如。:

2011-03-15

存储格式可以使用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 – format string which will be applied to the dict with keys year, month, and day.
  • regexp – regular expression which will be applied to incoming result rows. 如果正则表达式包含命名组,那么结果匹配字典作为关键字参数应用于Python date()构造函数。否则,如果使用位置组,date()构造函数将通过位置参数通过* map(int, match_obj.groups(0)) T0>。
class sqlalchemy.dialects.sqlite.TIME(*args, **kwargs)

基础:sqlalchemy.dialects.sqlite.base._DateTimeMixinsqlalchemy.types.Time

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

默认的字符串存储格式是:

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

例如。:

12:05:57.10558

存储格式可以使用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 – format string which will be applied to the dict with keys hour, minute, second, and microsecond.
  • regexp – regular expression which will be applied to incoming result rows. 如果regexp包含命名组,那么结果匹配字典作为关键字参数应用于Python time()构造函数。否则,如果使用位置组,time()构造函数将通过* map(int, match_obj.groups(0)) T0>。

Pysqlite ¶ T0>

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

请注意,pysqlite与Python发行版中包含的sqlite3模块是相同的驱动程序。

DBAPI ¶ T0>

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

连接¶ T0>

连接字符串:

sqlite+pysqlite:///file_path

驱动程序¶ T0>

当使用Python 2.5及以上版本时,已经安装了内置的sqlite3驱动程序,不需要额外的安装。否则,需要存在pysqlite2驱动程序。这是与sqlite3相同的驱动程序,只是名称不同而已。

pysqlite2驱动程序将首先加载,如果找不到,则加载sqlite3这允许明确安装的pysqlite驱动程序优先于内置的驱动程序。As with all dialects, a specific DBAPI module may be provided to create_engine() to control this explicitly:

from sqlite3 import dbapi2 as sqlite
e = create_engine('sqlite+pysqlite:///file.db', module=sqlite)

连接字符串

SQLite数据库的文件规范是作为URL的“数据库”部分。请注意,SQLAlchemy网址的格式是:

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://,而不是别的:

# in-memory database
e = create_engine('sqlite://')

与sqlite3“本地”日期和日期时间类型兼容

pysqlite驱动程序包括sqlite3.PARSE_DECLTYPES和sqlite3.PARSE_COLNAMES选项,这些选项会将显式转换为“date”或“timestamp”的任何列或表达式的效果转换为Python日期或日期时间对象。pysqlite方言提供的日期和日期时间类型目前与这些选项不兼容,因为它们呈现ISO日期/日期时间(包括微秒),而pysqlite的驱动程序却没有。此外,SQLAlchemy目前不会自动呈现独立函数“current_timestamp”和“current_date”所需的“强制转换”语法,以便本地返回datetime / date类型。不幸的是,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类型...困惑了吗?)将不执行任何绑定参数或结果处理。执行“func.current_date()”将返回一个字符串。“func.current_timestamp()”被注册为在SQLAlchemy中返回一个DATETIME类型,所以这个函数仍然接收SQLAlchemy级别的结果处理。

线程/池化行为

Pysqlite的默认行为是禁止在多个线程中使用单个连接。这本来是为了适应在不同情况下不支持多线程操作的旧版SQLite。特别是,较旧的SQLite版本在任何情况下都不允许在多线程中使用:memory:数据库。

Pysqlite确实包含了一个名为check_same_thread的现在未记录的标志,它会禁用这个检查,但是请注意,在多个线程中并发使用pysqlite连接仍然是不安全的。特别是,任何语句执行调用都需要外部互斥,因为Pysqlite不提供错误消息的线程安全传播等等。所以即使是:memory:数据库也可以在现代SQLite的线程之间共享,但是Pysqlite并没有提供足够的线程安全性来使这个用法值得。

SQLAlchemy设置池以使用Pysqlite的默认行为:

  • 当指定:memory: SQLite数据库时,缺省方言将使用SingletonThreadPool这个池每个线程维护一个连接,所以在当前线程中对引擎的所有访问使用相同的:memory:数据库 - 其他线程将访问不同的:memory:

  • 当指定基于文件的数据库时,方言将使用NullPool作为连接的来源。该池关闭并丢弃立即返回池的连接。基于SQLite文件的连接开销非常低,所以不需要池化。该方案还可以防止在另一个线程中再次使用连接,并且与SQLite的粗粒度文件锁定效果最佳。

    Changed in version 0.7: Default selection of NullPool for SQLite file-based databases. 以前的版本默认为所有SQLite数据库选择SingletonThreadPool

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

要在多线程场景中使用: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;超出这个数字,连接将以非确定的方式被关闭。

Unicode的¶ T0>

pysqlite驱动程序只返回结果集中的Python unicode对象,而不是普通的字符串,并且在所有情况下容纳绑定参数值中的unicode对象。无论使用哪种SQLAlchemy字符串类型,Python 2中的Python unicode都将基于字符串的结果值。The Unicode type should still be used to indicate those columns that require unicode, however, so that non-unicode values passed inadvertently will emit a warning. 如果包含非ASCII字符的非unicode字符串被传递,则Pysqlite将发出错误。

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

Database Locking Behavior / Concurrency部分中,我们引用了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.execute("BEGIN")

上面,我们拦截一个新的pysqlite连接,并禁用任何事务集成。Then, at the point at which SQLAlchemy knows that transaction scope is to begin, we emit "BEGIN" ourselves.

当我们控制"BEGIN"时,我们也可以直接控制在BEGIN TRANSACTION引入的SQLite锁定模式,在我们的"BEGIN"

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

也可以看看

BEGIN TRANSACTION - on the SQLite site

sqlite3 SELECT does not BEGIN a transaction - on the Python bug tracker

sqlite3 module breaks transactions and potentially corrupts data - on the Python bug tracker

Pysqlcipher ¶ T0>

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

pysqlcipher is a fork of the standard pysqlite driver to make use of the SQLCipher backend.

New in version 0.9.9.

DBAPI ¶ T0>

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

连接¶ T0>

连接字符串:

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

驱动程序¶ T0>

这里的驱动程序是使用SQLCipher引擎的pysqlcipher驱动程序。这个系统本质上引入了新的PRAGMA命令给SQLite,它允许设置密码和其他加密参数,允许数据库文件被加密。

连接字符串

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

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

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

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

https://www.zetetic.net/sqlcipher/sqlcipher-api/中记录的由SQLCipher支持的其他与加密相关的编译指示的选择可以在查询字符串中传递,并且会导致PRAGMA被称为每个新的连接。目前,支持cipherkdf_iter cipher_page_sizecipher_use_hmac

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

合并行为

驱动程序根据Threading/Pooling Behavior中的描述更改pysqlite的默认池行为。pysqlcipher驱动程序的连接速度比pysqlite驱动程序慢得多,很可能是由于加密开销,所以这里的方言默认使用SingletonThreadPool实现,而不是NullPool由pysqlite使用的池。As always, the pool implementation is entirely configurable using the create_engine.poolclass parameter; the StaticPool may be more feasible for single-threaded use, or NullPool may be used to prevent unencrypted connections from being held open for long periods of time, at the expense of slower startup time for new connections.