非常教程

Sqlite参考手册

C Interface: Session Module

The Session Extension

1.介绍

1.1. 典型用例

1.2. 获得会话扩展

1.3. 限制

2.概念

2.1. 变更集和补丁集

2.2. 冲突

2.3. 更改设置

3. 使用会话扩展

3.1. 捕获变更集

3.2. 将更改集应用于数据库

3.3. 检查变更集的内容

4. 扩展功能

会话扩展提供了一种机制,用于记录对SQLite数据库中部分或全部rowid表的更改,并将这些更改打包到“changeset”或“patchset”文件中,稍后可用于将相同的一组更改应用于另一个数据库具有相同的模式和兼容的起始数据。“changeset”也可能被倒置并用于“undo”会话。

本文档是对会话扩展的介绍。接口的细节在单独的会话扩展C语言接口文档中。

1.1. 典型用例

假设SQLite被用作特定设计应用程序的应用程序文件格式。两个用户,Alice和Bob,每个用户的基线设计大小都是千兆字节。他们整天都在同时工作,每个人都对设计进行自定义和调整。在一天结束时,他们希望将他们的变化合并为一个统一的设计。

会话扩展通过记录对Alice和Bob数据库的所有更改并将这些更改写入更改集或补丁集文件来实现此目的。在一天结束时,Alice可以将她的变更集发送给Bob,并且Bob可以将其应用到他的数据库中。结果(假设没有冲突)是Bob的数据库包含他的变更和Alice的变更。同样,Bob可以将其工作变更集发送给Alice,并且可以将其更改应用到她的数据库中。

换句话说,会话扩展为SQLite数据库文件提供了一个类似于unix修补程序(https://en.wikipedia.org/wiki/Patch_(Unix%29))实用程序或“merge”功能的工具的版本控制系统,如Fossil,Git或Mercurial。

1.2. 获得会话扩展

自版本3.13.0(2016-05-18)以来,会话扩展已包含在SQLite合并源分布中。默认情况下,会话扩展被禁用。要启用它,请使用以下编译器开关构建:

-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK

或者,如果使用autoconf构建系统,则将--enable-session选项传递给configure脚本。

1.3. 限制

  • 在SQLite版本3.17.0之前,会话扩展只能使用rowid表,而不能使用WITHOUT ROWID表。从3.17.0开始,支持rowid和WITHOUT ROWID表。
  • 不支持虚拟表。没有捕获虚拟表格的变化。
  • 会话扩展仅适用于具有声明的PRIMARY KEY的表。表的PRIMARY KEY可能是INTEGER PRIMARY KEY(rowid别名)或外部PRIMARY KEY。
  • SQLite允许将NULL值存储在PRIMARY KEY列中。但是,会话扩展会忽略所有这些行。会话模块不记录任何影响具有PRIMARY KEY列中的一个或多个NULL值的行的更改。

2.1. 变更集和补丁集

会话模块围绕创建和操作变更集进行。变更集是对数据库的一系列更改进行编码的一组数据。变更集中的每个更改都是以下之一:

  • 一个INSERT。INSERT更改包含要添加到数据库表的单个行。INSERT更改的有效负载由新行的每个字段的值组成。
  • 一个DELETE。DELETE更改表示一个由其主键值标识的行,以从数据库表中删除。DELETE更改的有效内容由已删除行的所有字段的值组成。
  • 一个UPDATE。UPDATE更改表示修改数据库表中由单个行的一个或多个非PRIMARY KEY字段标识的PRIMARY KEY字段。UPDATE更改的有效内容包括:
-  The PRIMARY KEY values identifying the modified row, 
-  The new values for each modified field of the row, and 
-  The original values for each modified field of the row. 

UPDATE更改不包含有关未被更改修改的非PRIMARY KEY字段的任何信息。UPDATE更改不能指定对PRIMARY KEY字段的修改。

单个变更集可能包含适用于多个数据库表的更改。对于每个更改集至少包含一个更改的表,它还会编码以下数据:

  • 数据库表的名称,
  • 表格的列数,以及
  • 哪些列是PRIMARY KEY列。

更改集只能应用于包含与存储在变更集中的上述三个条件相匹配的表的数据库。

补丁集与变更集类似。它比变更集略微更紧凑,但提供更有限的冲突检测和分辨率选项(有关详细信息,请参阅下一节)。补丁集和变更集之间的差异在于:

  • 对于DELETE更改,有效负载只包含PRIMARY KEY字段。其他字段的原始值不作为补丁集的一部分存储。
  • 对于UPDATE更改,有效负载仅包含PRIMARY KEY字段和修改字段的新值。修改字段的原始值不作为修补程序集的一部分存储。

2.2. 冲突

将更改集或补丁集应用于数据库时,会尝试为每个INSERT更改插入一个新行,为每个DELETE更改删除一行并为每个UPDATE更改修改一行。如果目标数据库与记录变更集的原始数据库处于相同状态,则这是一件简单的事情。但是,如果目标数据库的内容不完全处于此状态,则应用更改集或修补程序集时可能会发生冲突。

处理INSERT更改时,可能会发生以下冲突:

  • 目标数据库可能已经包含具有与INSERT更改所指定的PRIMARY KEY值相同的行。
  • 当插入新行时,可能会违反其他数据库约束,例如UNIQUE或CHECK约束。

处理DELETE更改时,可能会检测到以下冲突:

  • 目标数据库可能不包含具有要删除的指定PRIMARY KEY值的行。
  • 目标数据库可能包含具有指定PRIMARY KEY值的行,但其他字段可能包含与那些作为变更集的一部分存储的值不匹配的值。使用补丁集时不会检测到此类冲突。

处理UPDATE更改时,可能会检测到以下冲突:

  • 目标数据库可能不包含具有指定PRIMARY KEY值修改的行。
  • 目标数据库可能包含具有指定PRIMARY KEY值的行,但是将由修改修改的字段的当前值可能与变更集中存储的原始值不匹配。使用补丁集时不会检测到此类冲突。
  • 当行更新时,可能会违反其他数据库约束,例如UNIQUE或CHECK约束。

根据冲突的类型,会话应用程序具有各种可配置的处理冲突的选项,从忽略冲突变更,中止整个变更集应用程序或应用更改(尽管存在冲突)。有关详细信息,请参阅sqlite3changeset_apply() API的文档。

2.3. 更改设置

会话对象配置完成后,它开始监视对其配置表的更改。但是,每当数据库中的行被修改时,它都不会记录整个更改。相反,它只记录每个插入行的PRIMARY KEY字段,以及任何更新或删除行的PRIMARY KEY和所有原始行值。如果一行通过一次会话被多次修改,则不会记录新的信息。

创建变更集或补丁集所需的其他信息是在调用sqlite3session_changeset()或sqlite3session_patchset()时从数据库文件读取的。特别,

  • 对于作为INSERT操作结果记录的每个主键,会话模块会检查表中是否仍有匹配主键的行。如果是这样,则将INSERT更改添加到更改集。
  • 对于作为UPDATE或DELETE操作结果记录的每个主键,会话模块还会检查表中具有匹配主键的行。如果可以找到一个,但一个或多个非PRIMARY KEY字段与原始记录值不匹配,则将UPDATE添加到变更集。或者,如果指定的主键没有任何行,则将DELETE添加到变更集中。如果该行确实存在但没有任何非主键字段已被修改,则不会将更改添加到变更集。

其中一个含义是,如果在单个会话中进行更改并取消了更改(例如,如果插入了行然后再次删除),则会话模块根本不会报告任何更改。或者,如果某行在同一个会话中多次更新,则所有更新都会合并到任何更改集或修补程序集blob中的单个更新中。

本节提供了演示如何使用会话扩展的示例。

3.1. 捕获变更集

下面的示例代码演示了执行SQL命令时捕获变更集的步骤。综上所述:

  1. 会话对象(类型为sqlite3_session *)通过调用sqlite3session_create()API函数来创建。单个会话对象监视通过单个sqlite3 *数据库句柄对单个数据库(即“main”,“temp”或附加数据库)所做的更改。
  2. 会话对象配置有一组表来监视变化。默认情况下,会话对象不会监视任何数据库表上的更改。在此之前,它必须进行配置。有三种方法可以配置一组表来监视变化:
-  By explicitly specifying tables using one call to [sqlite3session\_attach()](session/sqlite3session_attach) for each table, or 
-  By specifying that all tables in the database should be monitored for changes using a call to [sqlite3session\_attach()](session/sqlite3session_attach) with a NULL argument, or 
-  By configuring a callback to be invoked the first time each table is written to that indicates to the session module whether or not changes on the table should be monitored. 

下面的示例代码使用上面枚举的第二个方法 - 它监视所有数据库表上的更改。

  1. 通过执行SQL语句对数据库进行更改。会话对象记录这些更改。
  2. 使用对sqlite3session_changeset()的调用从会话对象中提取变更集团块(或者,如果使用补丁集,则调用sqlite3session_patchset()函数)。
  3. 使用对sqlite3session_delete()API函数的调用删除会话对象。在从中提取变更集或补丁集后,不需要删除会话对象。它可以保留附加到数据库句柄,并将继续监视配置表上的更改。但是,如果在会话对象上第二次调用sqlite3session_changeset()或sqlite3session_patchset(),则更改集或补丁集将包含自会话创建以来连接上发生的所有更改。换句话说,会话对象不会通过调用sqlite3session_changeset()或sqlite3session_patchset()来重置或归零。
/*
** Argument zSql points to a buffer containing an SQL script to execute 
** against the database handle passed as the first argument. As well as
** executing the SQL script, this function collects a changeset recording
** all changes made to the "main" database file. Assuming no error occurs,
** output variables (*ppChangeset) and (*pnChangeset) are set to point
** to a buffer containing the changeset and the size of the changeset in
** bytes before returning SQLITE_OK. In this case it is the responsibility
** of the caller to eventually free the changeset blob by passing it to
** the sqlite3_free function.
**
** Or, if an error does occur, return an SQLite error code. The final
** value of (*pChangeset) and (*pnChangeset) are undefined in this case.
*/
int sql_exec_changeset(
  sqlite3 *db,                  /* Database handle */
  const char *zSql,             /* SQL script to execute */
  int *pnChangeset,             /* OUT: Size of changeset blob in bytes */
  void **ppChangeset            /* OUT: Pointer to changeset blob */
){
  sqlite3_session *pSession = 0;
  int rc;

  /* Create a new session object */
  rc = sqlite3session_create(db, "main", &pSession);

  /* Configure the session object to record changes to all tables */
  if( rc==SQLITE_OK ) rc = sqlite3session_attach(pSession, NULL);

  /* Execute the SQL script */
  if( rc==SQLITE_OK ) rc = sqlite3_exec(db, zSql, 0, 0, 0);

  /* Collect the changeset */
  if( rc==SQLITE_OK ){
    rc = sqlite3session_changeset(pSession, pnChangeset, ppChangeset);
  }

  /* Delete the session object */
  sqlite3session_delete(pSession);

  return rc;
}

3.2. 将更改集应用于数据库

将变更集应用于数据库比捕获变更集要简单。通常,对sqlite3changeset_apply()的单个调用就足够了,如下面的示例代码所示。

在情况复杂的情况下,应用变更集的复杂性在于解决冲突。有关详细信息,请参阅上面链接的API文档。

/*
** Conflict handler callback used by apply_changeset(). See below.
*/
static int xConflict(void *pCtx, int eConflict, sqlite3_changset_iter *pIter){
  int ret = (int)pCtx;
  return ret;
}

/*
** Apply the changeset contained in blob pChangeset, size nChangeset bytes,
** to the main database of the database handle passed as the first argument.
** Return SQLITE_OK if successful, or an SQLite error code if an error
** occurs.
**
** If parameter bIgnoreConflicts is true, then any conflicting changes 
** within the changeset are simply ignored. Or, if bIgnoreConflicts is
** false, then this call fails with an SQLTIE_ABORT error if a changeset
** conflict is encountered.
*/
int apply_changeset(
  sqlite3 *db,                  /* Database handle */
  int bIgnoreConflicts,         /* True to ignore conflicting changes */
  int nChangeset,               /* Size of changeset in bytes */
  void *pChangeset              /* Pointer to changeset blob */
){
  return sqlite3changeset_apply(
      db, 
      nChangeset, pChangeset, 
      0, xConflict, 
      (void*)bIgnoreConflicts
  );
}

3.3.检查变更集的内容

下面的示例代码演示了用于迭代并提取与变更集中所有更改相关的数据的技术。总结:

  1. 调用sqlite3changeset_start()API来创建和初始化迭代器以遍历变更集的内容。最初,迭代器根本没有指向任何元素。
  2. 在迭代器上对sqlite3changeset_next()的第一次调用将它移动到指向变更集中的第一个变更(如果变更集完全为空,则指向EOF)。如果sqlite3changeset_next()将迭代器移动到指向有效条目时返回SQLITE_ROW,如果将迭代器移动到EOF,则返回SQLITE_DONE;如果发生错误,则返回SQLite错误代码。
  3. 如果迭代器指向有效条目,则sqlite3changeset_op()API可用于确定迭代器指向的更改类型(INSERT,UPDATE或DELETE)。此外,可以使用相同的API来获取更改所适用的表的名称及其预期的列数和主键列数。
  4. 如果迭代器指向有效的INSERT或UPDATE条目,则sqlite3changeset_new()API可用于获取更改有效内容中的new.*值。
  5. 如果迭代器指向有效的DELETE或UPDATE条目,则sqlite3changeset_old()API可用于获取更改有效内容中的new.*值。
  6. 使用对sqlite3changeset_finalize()API的调用删除迭代器。如果迭代时发生错误,则返回SQLite错误代码(即使sqlite3changeset_next()已经返回相同的错误代码)。或者,如果没有发生错误,则返回SQLITE_OK。
/*
** Print the contents of the changeset to stdout.
*/
static int print_changeset(void *pChangeset, int nChangeset){
  int rc;
  sqlite3_changeset_iter *pIter = 0;

  /* Create an iterator to iterate through the changeset */
  rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
  if( rc!=SQLITE_OK ) return rc;

  /* This loop runs once for each change in the changeset */
  while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
    const char *zTab;           /* Table change applies to */
    int nCol;                   /* Number of columns in table zTab */
    int op;                     /* SQLITE_INSERT, UPDATE or DELETE */
    sqlite3_value *pVal;

    /* Print the type of operation and the table it is on */
    rc = sqlite3changeset_op(pIter, &zTab, &nCol, &op, 0);
    if( rc!=SQLITE_OK ) goto exit_print_changeset;
    printf("%s on table %s\n",
      op==SQLITE_INSERT?"INSERT" : op==SQLITE_UPDATE?"UPDATE" : "DELETE",
      zTab
    );

    /* If this is an UPDATE or DELETE, print the old.* values */
    if( op==SQLITE_UPDATE || op==SQLITE_DELETE ){
      printf("Old values:");
      for(i=0; i<nCol; i++){
        rc = sqlite3changeset_old(pIter, i, &pVal);
        if( rc!=SQLITE_OK ) goto exit_print_changeset;
        printf(" %s", pVal ? sqlite3_value_text(pVal) : "-");
      }
      printf("\n");
    }

    /* If this is an UPDATE or INSERT, print the new.* values */
    if( op==SQLITE_UPDATE || op==SQLITE_INSERT ){
      printf("New values:");
      for(i=0; i<nCol; i++){
        rc = sqlite3changeset_new(pIter, i, &pVal);
        if( rc!=SQLITE_OK ) goto exit_print_changeset;
        printf(" %s", pVal ? sqlite3_value_text(pVal) : "-");
      }
      printf("\n");
    }
  }

  /* Clean up the changeset and return an error code (or SQLITE_OK) */
 exit_print_changeset:
  rc2 = sqlite3changeset_finalize(pIter);
  if( rc==SQLITE_OK ) rc = rc2;
  return rc;
}

大多数应用程序仅使用前一节中介绍的会话模块功能。但是,以下附加功能可用于更改集和补丁集blob的使用和操作:

  • 两个或多个变更集/补丁集可以使用sqlite3changeset_concat()或sqlite3_changegroup接口进行组合。
  • 变更集可以使用sqlite3changeset_invert()API函数“inverted”。一个倒置的变化集解除了原来的变化。如果changeset C +与changeset C相反,那么将C和C +应用于数据库应该保持数据库不变。
Sqlite

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来

主页 https://sqlite.org/
源码 https://www.sqlite.org/src/
发布版本 3.21.0

Sqlite目录

1.C界面 | C Interface
2.C Interface: Session Module
3.CLI
4.数据库文件表 | Database File Format
5.数据类 | Datatypes
6.动态内存分配 | Dynamic Memory Allocation
7.外键约束 | Foreign Key Constraints
8.全文索引 | Full-Text Search
9.损坏方式 | How To Corrupt
10.JSON
11.语言 | Language
12.局限性 | Limits
13.锁定和并发 | Locking and Concurrency
14.其他 | Miscellaneous
15.PRAGMA Statements
16.查询计划程序 | Query Planner
17.R*Tree Module
18.RBU Extension
19.语法图 | Syntax Diagrams
20.Tcl Interface
21.虚拟表机制 | Virtual Table Mechanism
22.预写日志 | Write-Ahead Logging
23.SQL 教程
24.SQL 简介
25.SQL 语法
26.SQL DELETE 语句
27.SQL UPDATE 语句
28.SQL NOT NULL 约束
29.SQL 约束
30.SQL CREATE TABLE 语句
31.SQL CREATE DATABASE 语句
32.SQL INSERT INTO SELECT 语句
33.SQL SELECT INTO 语句
34.SQL CREATE VIEW、REPLACE VIEW、 DROP VIEW 语句
35.SQL AUTO INCREMENT 字段
36.SQL ALTER TABLE 语句
37.SQL 撤销索引、表以及数据库
38.SQL CREATE INDEX 语句
39.SQL DEFAULT 约束
40.SQL CHECK 约束
41.SQL FOREIGN KEY 约束
42.SQL PRIMARY KEY 约束
43.SQL UNIQUE 约束
44.SQL 通用数据类型
45.SQL ISNULL()、NVL()、IFNULL() 和 COALESCE() 函数
46.SQL NULL 值 – IS NULL 和 IS NOT NULL
47.SQL Server 和 MySQL 中的 Date 函数
48.SQL MS Access、MySQL 和 SQL Server 数据类型
49.SQL 函数
50.SQL 总结
51.SQL 主机
52.SQL 快速参考
53.SQL ROUND() 函数
54.SQL Server GETDATE() 函数
55.MySQL DATE_FORMAT() 函数
56.MySQL DATEDIFF() 函数
57.MySQL DATE_SUB() 函数
58.MySQL DATE_ADD() 函数
59.MySQL EXTRACT() 函数
60.MySQL DATE() 函数
61.MySQL CURTIME() 函数
62.MySQL CURDATE() 函数
63.MySQL NOW() 函数
64.SQL Server CONVERT() 函数
65.SQL Server DATEDIFF() 函数
66.SQL Server DATEADD() 函数
67.SQL Server DATEPART() 函数
68.SQLite 命令
69.SQLite 安装
70.SQLite 简介
71.SQLite 运算符
72.SQLite Select 语句
73.SQLite 删除表
74.SQLite 创建表
75.SQLite Insert 语句
76.SQLite 分离数据库
77.SQLite 附加数据库
78.SQLite 创建数据库
79.SQLite 数据类型
80.SQLite 语法
81.SQLite Order By
82.SQLite Limit 子句
83.SQLite Glob 子句
84.SQLite Like 子句
85.SQLite Delete 语句
86.SQLite Update 语句
87.SQLite AND/OR 运算符
88.SQLite Where 子句
89.SQLite 表达式
90.SQLite Distinct 关键字
91.SQLite Having 子句
92.SQLite Group By
93.SQLite Join
94.SQLite 约束
95.SQLite PRAGMA
96.SQLite 事务
97.SQLite 视图
98.SQLite Truncate Table
99.SQLite Alter 命令
100.SQLite Indexed By