Transaction

1.事务控制语法

开始stmt:

BEGIN EXCLUSIVE TRANSACTION DEFERRED IMMEDIATE

提交stmt:

COMMIT TRANSACTION END

回滚stmt:

ROLLBACK TRANSACTION TO SAVEPOINT savepoint-name

2. Transactions

除非在事务内,否则不会发生读取或写入。任何访问数据库的命令(基本上,任何 SQL 命令,除了一些PRAGMA语句)将自动启动一个事务,如果一个事务尚未生效。自动启动的事务在最后一个 SQL 语句完成时提交。

可以使用 BEGIN 命令手动启动事务。此类事务通常会持续到下一个 COMMIT 或 ROLLBACK 命令。但是,如果数据库关闭或发生错误并且指定了 ROLLBACK 冲突解决算法,事务也会 ROLLBACK。 有关ROLLBACK 冲突解决算法的更多信息, 请参阅有关ON CONFLICT子句的文档。

END TRANSACTION 是 COMMIT 的别名。

使用 BEGIN...COMMIT 创建的事务不嵌套。对于嵌套事务,使用SAVEPOINTRELEASE命令。上面语法图中显示的 ROLLBACK 命令的“TO SAVEPOINT name ”子句仅适用于SAVEPOINT 事务。尝试在事务中调用 BEGIN 命令将失败并出现错误,无论事务是由SAVEPOINT还是之前的 BEGIN 启动的。不带 TO 子句的 COMMIT 命令和 ROLLBACK 命令对SAVEPOINT事务的作用与它们对由 BEGIN 启动的事务的作用相同。

2.1. 读取事务与写入事务

SQLite 支持来自不同数据库连接的多个同时读取事务,可能在不同的线程或进程中,但只支持一个同时写入事务。

读取事务仅用于读取。写事务允许读取和写入。读事务由 SELECT 语句启动,写事务由 CREATE、DELETE、DROP、INSERT 或 UPDATE 等语句(统称为“写语句”)启动。如果在读事务处于活动状态时发生写语句,则如果可能,读事务将升级为写事务。如果其他一些数据库连接已经修改了数据库或者已经在修改数据库的过程中,那么升级到写事务是不可能的,并且写语句将失败并返回 SQLITE_BUSY

当读取事务处于活动状态时,由单独的数据库连接实现的对数据库的任何更改都不会被启动读取事务的数据库连接看到。如果数据库连接 X 持有一个读事务,那么当 X 的事务仍然打开时,其他一些数据库连接 Y 可能会更改数据库的内容,但是在事务结束之前 X 将无法看到这些更改。当其读取事务处于活动状态时,X 将继续查看 Y 实施更改之前的数据库历史快照。

2.2. 延期、即时和独家交易

事务可以是延迟的、立即的或独占的。默认事务行为是延迟的。

DEFERRED 意味着直到第一次访问数据库时事务才真正开始。在内部,BEGIN DEFERRED 语句只是在数据库连接上设置一个标志,关闭通常在最后一个语句完成时发生的自动提交。这会导致自动启动的事务一直持续到显式 COMMIT 或 ROLLBACK,或者直到错误或 ON CONFLICT ROLLBACK 子句引发回滚。如果 BEGIN DEFERRED 之后的第一条语句是 SELECT,那么将启动读取事务。如果可能,后续的写语句会将事务升级为写事务,或者返回 SQLITE_BUSY。如果 BEGIN DEFERRED 之后的第一条语句是写语句,那么将启动一个写事务。

IMMEDIATE 导致数据库连接立即开始新的写入,而不等待写入语句。如果另一个写事务已经在另一个数据库连接上处于活动状态, BEGIN IMMEDIATE 可能会失败并返回 SQLITE_BUSY 。

EXCLUSIVE 与 IMMEDIATE 的相似之处在于立即启动写入事务。EXCLUSIVE 和 IMMEDIATE 在WAL 模式下是一样的,但在其他日志模式下,EXCLUSIVE 会阻止其他数据库连接在事务进行时读取数据库。

2.3. 隐式与显式事务

隐式事务(自动启动的事务,不是由 BEGIN 启动的事务)在最后一个活动语句完成时自动提交。一条语句在其最后一个游标关闭时结束,这保证在准备好的语句被重置完成时发生。出于事务控制的目的,某些语句可能会在重置或最终确定之前“完成”,但不能保证这一点。确保语句“完成”的唯一方法是对该语句调用sqlite3_reset()sqlite3_finalize()用于增量 BLOB I/O的打开的sqlite3_blob也算作未完成的语句。sqlite3_blob _关闭时完成

显式 COMMIT 命令会立即运行,即使有未决的SELECT语句也是如此。但是,如果有挂起的写操作,则 COMMIT 命令将失败,错误代码为SQLITE_BUSY

如果另一个线程或进程具有打开的读取连接,则 尝试执行 COMMIT 也可能导致SQLITE_BUSY返回代码。当 COMMIT 以这种方式失败时,事务保持活动状态,稍后可以在读者有机会清除后重试 COMMIT。

在非常旧的 SQLite 版本中(版本 3.7.11 - 2012-03-20 之前),如果有任何待处理的查询 ,ROLLBACK 将失败并返回错误代码 SQLITE_BUSY 。在较新版本的 SQLite 中,ROLLBACK 将继续进行,挂起的语句通常会中止,导致它们返回SQLITE_ABORTSQLITE_ABORT_ROLLBACK错误。在 SQLite 版本 3.8.8 (2015-01-16) 及更高版本中,只要 ROLLBACK 不修改数据库模式,挂起的读取将在 ROLLBACK 之后继续运行。

如果PRAGMA journal_mode设置为 OFF(从而禁用回滚日志文件),则 ROLLBACK 命令的行为未定义。

3.对交易中错误的响应

如果事务中发生某些类型的错误,事务可能会或可能不会自动回滚。可能导致自动回滚的错误包括:

对于所有这些错误,SQLite 会尝试仅撤消它正在处理的一条语句,并保持同一事务中先前语句的更改不变,然后继续该事务。但是,根据正在评估的语句和错误发生的时间点,SQLite 可能需要回滚并取消整个事务。应用程序可以通过使用sqlite3_get_autocommit() C 语言接口来判断 SQLite 执行的操作过程 。

建议应用程序通过显式发出 ROLLBACK 命令来响应上面列出的错误。如果事务已被错误响应自动回滚,则 ROLLBACK 命令将失败并返回错误,但这不会造成任何危害。

SQLite 的未来版本可能会扩展可能导致自动事务回滚的错误列表。SQLite 的未来版本可能会更改错误响应。特别是,我们可能会选择通过导致上述错误强制无条件回滚来简化 SQLite 未来版本中的接口。