小巧。快速。可靠。
三选二。

SQLite 会话模块 C/C++ 接口

此页面定义了 SQLite 会话扩展 的 C 语言接口。这不是教程。这些页面旨在精确,而不是易于阅读。教程单独提供

此页面在一个 HTML 文件中包含所有 C 语言接口信息。如果您愿意,相同的信息也可以分解成一些较小的页面以便于查看。

此文档由一个扫描源代码文件 sqlite3session.h 中注释的脚本创建。


对象


常量


函数


sqlite3changeset_start_v2 的标志

#define SQLITE_CHANGESETSTART_INVERT        0x0002

以下标志可以通过 sqlite3changeset_start_v2sqlite3changeset_start_v2_strm 的第 4 个参数传递。

SQLITE_CHANGESETAPPLY_INVERT
在迭代变更集时反转变更集。这等效于在应用变更集之前使用 sqlite3changeset_invert() 反转变更集。对于修补集指定此标志是错误的。


sqlite3session_config() 的值。

#define SQLITE_SESSION_CONFIG_STRMSIZE 1


变更组句柄

typedef struct sqlite3_changegroup sqlite3_changegroup;

变更组是一个用于组合两个或多个变更集修补集的对象。

构造函数:sqlite3changegroup_new()

析构函数:sqlite3changegroup_delete()

方法:sqlite3changegroup_add()sqlite3changegroup_add_change()sqlite3changegroup_output()


变更集迭代器句柄

typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;

此对象的实例充当游标,用于迭代变更集修补集的元素。

构造函数:sqlite3changeset_start()sqlite3changeset_start_v2()


重新调整变更集

typedef struct sqlite3_rebaser sqlite3_rebaser;

重要提示:此接口是实验性的,如有更改,恕不另行通知。

假设有一个站点托管处于状态 S0 的数据库。并且进行了一些修改,将该数据库移动到状态 S1 并记录了一个变更集(“本地”变更集)。然后,从另一个站点接收一个基于 S0 的变更集(“远程”变更集)并将其应用到数据库。然后数据库处于状态 (S1+"远程"),其中确切的状态取决于在应用“远程”时做出的任何冲突解决决策 (OMIT 或 REPLACE)。重新调整变更集是为了更新它以考虑这些冲突解决决策,以便不必在网络中的其他地方解决相同的冲突。

例如,如果本地和远程变更集都在“CREATE TABLE t1(a PRIMARY KEY, b)”上包含相同键的 INSERT

本地:INSERT INTO t1 VALUES(1, 'v1');远程:INSERT INTO t1 VALUES(1, 'v2');

并且冲突解决是 REPLACE,则从本地变更集中删除 INSERT 变更(它被覆盖)。或者,如果冲突解决是“OMIT”,则本地变更集被修改为包含

UPDATE t1 SET b = 'v2' WHERE a=1;

本地变更集中的更改按如下方式重新调整

本地 INSERT
这可能仅与远程 INSERT 冲突。如果冲突解决是 OMIT,则向重新调整的变更集中添加 UPDATE 变更。或者,如果冲突解决是 REPLACE,则不向重新调整的变更集中添加任何内容。

本地 DELETE
这可能与远程 UPDATE 或 DELETE 冲突。在这两种情况下,唯一可能的解决方法是 OMIT。如果远程操作是 DELETE,则不向重新调整的变更集中添加任何更改。如果远程操作是 UPDATE,则更改的 old.* 字段将更新为反映 UPDATE 中的 new.* 值。

本地 UPDATE
这可能与远程 UPDATE 或 DELETE 冲突。如果它与 DELETE 冲突,并且冲突解决是 OMIT,则更新将更改为 INSERT。更新更改中的 new.* 记录中任何未定义的值都将使用冲突 DELETE 中的 old.* 值填充。或者,如果冲突解决是 REPLACE,则 UPDATE 更改将简单地从重新调整的变更集中省略。

如果与远程 UPDATE 冲突,并且解决方案是 OMIT,则使用远程更改中的 new.* 值重新调整 old.* 值。或者,如果解决方案是 REPLACE,则将更改复制到重新调整的变更集中,并删除远程 UPDATE 也更新的列的更新。如果这意味着不会更新任何列,则会省略更改。

本地更改可以同时针对多个远程更改重新调整。如果单个键由多个远程变更集修改,则在重新调整本地变更集之前按如下方式组合它们

请注意,来自多个远程变更集的冲突解决是按字段而非按行组合的。这意味着在多个远程 UPDATE 操作的情况下,单个本地更改的某些字段可能会针对 REPLACE 重新调整,而其他字段可能会针对 OMIT 重新调整。

为了重新调整本地变更集,必须首先使用 sqlite3changeset_apply_v2() 将远程变更集应用于本地数据库并捕获重新调整信息的缓冲区。然后

  1. 通过调用 sqlite3rebaser_create() 创建 sqlite3_rebaser 对象。
  2. 使用从 sqlite3changeset_apply_v2() 获得的重新调整缓冲区通过调用 sqlite3rebaser_configure() 配置新对象。如果要针对多个远程变更集重新调整本地变更集,则应多次调用 sqlite3rebaser_configure(),并且顺序应与多次调用 sqlite3changeset_apply_v2() 的顺序相同。
  3. 通过调用 sqlite3rebaser_rebase() 重新调整每个本地变更集。
  4. 通过调用 sqlite3rebaser_delete() 删除 sqlite3_rebaser 对象。


会话对象句柄

typedef struct sqlite3_session sqlite3_session;

此对象的实例是一个会话,可用于记录对数据库的更改。

构造函数:sqlite3session_create()

析构函数:sqlite3session_delete()


将变更集添加到变更组

int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);

将缓冲区 pData(大小为 nData 字节)中变更集(或修补集)中的所有更改添加到变更组。

如果缓冲区包含修补集,则对同一变更组对象进行的此函数的所有先前调用也必须指定了修补集。或者,如果缓冲区包含变更集,则此函数的早期调用也必须包含变更集。否则,将返回 SQLITE_ERROR,并且不会将任何更改添加到变更组。

变更集和变更组中的行由其主键列中的值标识。如果两行具有相同的主键,则变更集中的更改被认为应用于与变更组中已存在的更改相同的行。

对变更组中尚不存在的行所做的更改将简单地复制到其中。或者,如果新的变更集和变更组都包含应用于单行的更改,则变更组的最终内容取决于每种更改的类型,如下所示

现有更改 新更改 输出更改
INSERTINSERT忽略新更改。如果在已添加到变更组的变更集之后立即记录了新的变更集,则不会发生这种情况。
INSERTUPDATEINSERT 更改保留在变更组中。INSERT 更改中的值将被修改,就好像行是由现有更改插入的,然后根据新更改进行更新。
INSERTDELETE从变更组中删除现有的 INSERT。不会添加 DELETE。
UPDATEINSERT忽略新更改。如果在已添加到变更组的变更集之后立即记录了新的变更集,则不会发生这种情况。
UPDATEUPDATE现有的 UPDATE 保留在变更组中。它已进行修改,以便伴随的值就好像行先由现有更改更新,然后由新更改再次更新。
UPDATEDELETE现有的 UPDATE 将由变更组中的新 DELETE 替换。
DELETEINSERT如果新更改中插入的行中的一列或多列值与现有更改删除的行中的值不同,则现有的 DELETE 将由变更组中的 UPDATE 替换。否则,如果插入的行与删除的行完全相同,则现有的 DELETE 将被简单地丢弃。
DELETEUPDATE忽略新更改。如果在已添加到变更组的变更集之后立即记录了新的变更集,则不会发生这种情况。
DELETEDELETE忽略新更改。如果在已添加到变更组的变更集之后立即记录了新的变更集,则不会发生这种情况。

如果新的变更集包含对变更组中已存在的表的更改,则表的列数和主键列的位置必须一致。如果不是这种情况,则此函数将失败并返回 SQLITE_SCHEMA。但是,如果变更组对象已使用 sqlite3changegroup_schema() API 配置了数据库模式,则可以组合具有不同列数的变更集以用于单个表,前提是它们在其他方面兼容。

如果输入的变更集似乎已损坏并且检测到损坏,则返回 SQLITE_CORRUPT。或者,如果在处理过程中发生内存不足的情况,则此函数返回 SQLITE_NOMEM。

在所有情况下,如果发生错误,则最终变更组内容的状态未定义。如果未发生错误,则返回 SQLITE_OK。


将单个更改添加到变更组

int sqlite3changegroup_add_change(
  sqlite3_changegroup*,
  sqlite3_changeset_iter*
);

此函数将当前由作为第二个参数传递的迭代器指示的单个更改添加到变更组对象。添加更改的规则与sqlite3changegroup_add()中描述的规则相同。

如果更改已成功添加到变更组,则返回 SQLITE_OK。否则,将返回 SQLite 错误代码。

调用此函数时,迭代器必须指向一个有效的条目。如果它没有指向有效的条目,则返回SQLITE_ERROR,并且不会对变更组进行任何更改。此外,迭代器不能使用SQLITE_CHANGESETAPPLY_INVERT标志打开。在这种情况下,也会返回SQLITE_ERROR。


删除变更组对象

void sqlite3changegroup_delete(sqlite3_changegroup*);


创建新的变更组对象

int sqlite3changegroup_new(sqlite3_changegroup **pp);

sqlite3_changegroup对象用于将两个或多个变更集(或补丁集)组合成一个变更集(或补丁集)。单个变更组对象可以组合变更集或补丁集,但不能同时组合两者。输出始终与输入采用相同的格式。

如果成功,则此函数返回SQLITE_OK,并在返回前使用指向新的sqlite3_changegroup对象的指针填充(*pp)。调用方最终应使用对sqlite3changegroup_delete()的调用释放返回的对象。如果发生错误,则返回SQLite错误代码(即SQLITE_NOMEM),并将*pp设置为NULL。

sqlite3_changegroup对象的常用使用模式如下所示

在对new()和delete()的调用之间,可以进行任意数量的add()和output()调用,并且可以按任意顺序进行。

除了常规的sqlite3changegroup_add()和sqlite3changegroup_output()函数之外,还提供流式版本sqlite3changegroup_add_strm()和sqlite3changegroup_output_strm()。


从变更组中获取组合变更集

int sqlite3changegroup_output(
  sqlite3_changegroup*,
  int *pnData,                    /* OUT: Size of output buffer in bytes */
  void **ppData                   /* OUT: Pointer to output buffer */
);

获取包含表示变更组当前内容的变更集(或补丁集)的缓冲区。如果变更组的输入本身是变更集,则输出是变更集。或者,如果输入是补丁集,则输出也是补丁集。

与sqlite3session_changeset()和sqlite3session_patchset()函数的输出一样,此函数的输出中将所有与单个表相关的更改组合在一起。表以添加到变更组的第一个变更集中出现的相同顺序出现。如果添加到变更组的第二个或后续变更集包含对第一个变更集中未出现的表的更改,则将它们附加到输出变更集的末尾,并且再次按第一次遇到的顺序进行。

如果发生错误,则返回SQLite错误代码,并将输出变量(*pnData)和(*ppData)设置为0。否则,返回SQLITE_OK,并将输出变量分别设置为输出缓冲区的大小和指向输出缓冲区的指针。在这种情况下,调用方有责任最终使用对sqlite3_free()的调用释放缓冲区。


向变更组添加模式

int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);

此方法可用于选择性地强制执行规则,即添加到变更组句柄的变更集必须与数据库zDb(“main”,“temp”或附加数据库的名称)的模式匹配。如果调用sqlite3changegroup_add()以添加与配置的模式不兼容的变更集,则返回SQLITE_SCHEMA,并且变更组对象将处于未定义状态。

变更集模式被认为与数据库模式兼容的方式与sqlite3changeset_apply()相同。具体来说,对于变更集中的每个表,都存在一个数据库表,其中

变更组对象的输出始终与使用此函数指定的数据库具有相同的模式。在传递给sqlite3changegroup_add()的变更集的列数少于数据库模式中相应表的列数的情况下,将使用数据库模式中的默认列值填充这些列。这使得可以在变更组中组合具有单个表的不同列数的变更集成为可能,前提是它们在其他方面兼容。


连接两个变更集对象

int sqlite3changeset_concat(
  int nA,                         /* Number of bytes in buffer pA */
  void *pA,                       /* Pointer to buffer containing changeset A */
  int nB,                         /* Number of bytes in buffer pB */
  void *pB,                       /* Pointer to buffer containing changeset B */
  int *pnOut,                     /* OUT: Number of bytes in output changeset */
  void **ppOut                    /* OUT: Buffer containing output changeset */
);

此函数用于将两个变更集A和B连接成一个变更集。结果等效于先应用变更集A,然后应用变更集B。

此函数使用sqlite3_changegroup对象组合两个输入变更集。调用它会产生与以下代码片段类似的结果

  sqlite3_changegroup *pGrp;
  rc = sqlite3_changegroup_new(&pGrp);
  if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
  if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB);
  if( rc==SQLITE_OK ){
    rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
  }else{
    *ppOut = 0;
    *pnOut = 0;
  }

有关详细信息,请参阅下面的sqlite3_changegroup文档。


从变更集迭代器中获取冲突的行值

int sqlite3changeset_conflict(
  sqlite3_changeset_iter *pIter,  /* Changeset iterator */
  int iVal,                       /* Column number */
  sqlite3_value **ppValue         /* OUT: Value from conflicting row */
);

此函数仅应与传递给冲突处理程序回调的迭代器对象一起使用,该迭代器对象由sqlite3changeset_apply()SQLITE_CHANGESET_DATASQLITE_CHANGESET_CONFLICT一起传递。如果此函数在任何其他迭代器上调用,则返回SQLITE_MISUSE,并将*ppValue设置为NULL。

参数iVal必须大于或等于0,且小于受当前更改影响的表中的列数。否则,返回SQLITE_RANGE,并将*ppValue设置为NULL。

如果成功,则此函数将*ppValue设置为指向受保护的sqlite3_value对象,该对象包含与当前冲突处理程序回调关联的“冲突行”中的第iVal个值,并返回SQLITE_OK。

如果发生其他错误(例如OOM条件),则返回SQLite错误代码,并将*ppValue设置为NULL。


完成变更集迭代器

int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);

此函数用于完成使用sqlite3changeset_start()分配的迭代器。

此函数只能在使用sqlite3changeset_start()函数创建的迭代器上调用。如果应用程序使用由sqlite3changeset_apply()传递给冲突处理程序的迭代器调用此函数,则立即返回SQLITE_MISUSE,并且调用无效。

如果在调用sqlite3changeset_xxx()函数时遇到错误(例如,sqlite3changeset_next()中的SQLITE_CORRUPTsqlite3changeset_new()中的SQLITE_NOMEM),则此函数将返回与该错误对应的错误代码。否则,返回SQLITE_OK。这是为了允许以下模式(伪代码)

  sqlite3changeset_start();
  while( SQLITE_ROW==sqlite3changeset_next() ){
    // Do something with change.
  }
  rc = sqlite3changeset_finalize();
  if( rc!=SQLITE_OK ){
    // An error has occurred 
  }


确定外键约束违规的数量

int sqlite3changeset_fk_conflicts(
  sqlite3_changeset_iter *pIter,  /* Changeset iterator */
  int *pnOut                      /* OUT: Number of FK violations */
);

此函数只能与传递给SQLITE_CHANGESET_FOREIGN_KEY冲突处理程序回调的迭代器一起使用。在这种情况下,它将输出变量设置为目标数据库中已知外键违规的总数,并返回SQLITE_OK。

在所有其他情况下,此函数都返回SQLITE_MISUSE。


反转变更集

int sqlite3changeset_invert(
  int nIn, const void *pIn,       /* Input changeset */
  int *pnOut, void **ppOut        /* OUT: Inverse of input */
);

此函数用于“反转”变更集对象。将反转的变更集应用于数据库会反转应用未反转的变更集的效果。具体来说

此函数不会更改变更集内变更出现的顺序。它只是反转每个单独更改的含义。

如果成功,则将指向包含反转变更集的缓冲区的指针存储在*ppOut中,将相同缓冲区的大小存储在*pnOut中,并返回SQLITE_OK。如果发生错误,则*pnOut和*ppOut都将清零,并返回SQLite错误代码。

在成功调用此函数后,调用方有责任最终对*ppOut指针调用sqlite3_free()以释放缓冲区分配。

警告/待办事项:此函数目前假设输入是有效的变更集。如果不是,则结果未定义。


从变更集迭代器中获取new.*值

int sqlite3changeset_new(
  sqlite3_changeset_iter *pIter,  /* Changeset iterator */
  int iVal,                       /* Column number */
  sqlite3_value **ppValue         /* OUT: New value (or NULL pointer) */
);

传递给此函数的pIter参数可以是传递给冲突处理程序的迭代器sqlite3changeset_apply(),也可以是sqlite3changeset_start()创建的迭代器。在后一种情况下,对sqlite3changeset_next()的最近一次调用必须返回SQLITE_ROW。此外,只能在迭代器当前指向的更改类型为SQLITE_UPDATESQLITE_INSERT时调用它。否则,此函数返回SQLITE_MISUSE并将*ppValue设置为NULL。

参数iVal必须大于或等于0,且小于受当前更改影响的表中的列数。否则,返回SQLITE_RANGE,并将*ppValue设置为NULL。

如果成功,则此函数将*ppValue设置为指向受保护的sqlite3_value对象,该对象包含存储为UPDATE或INSERT更改的一部分的新行值向量中的第iVal个值,并返回SQLITE_OK。如果更改是UPDATE并且不包含请求列的新值,则*ppValue设置为NULL并返回SQLITE_OK。函数的名称来自这样一个事实,即这类似于更新或删除触发器可用的“new.*”列。

如果发生其他错误(例如OOM条件),则返回SQLite错误代码,并将*ppValue设置为NULL。


推进变更集迭代器

int sqlite3changeset_next(sqlite3_changeset_iter *pIter);

此函数只能与函数sqlite3changeset_start()创建的迭代器一起使用。如果在由sqlite3changeset_apply()传递给冲突处理程序回调的迭代器上调用它,则返回SQLITE_MISUSE,并且调用无效。

在使用sqlite3changeset_start()创建迭代器后,它立即不指向变更集中的任何更改。假设变更集不为空,则对该函数的第一次调用会将迭代器推进到指向变更集中的第一个更改。每次后续调用都会将迭代器推进到指向变更集中的下一个更改(如果有)。如果未发生错误并且迭代器在调用sqlite3changeset_next()推进它后指向有效的更改,则返回SQLITE_ROW。否则,如果已经访问了变更集中的所有更改,则返回SQLITE_DONE。

如果发生错误,则返回SQLite错误代码。可能的错误代码包括SQLITE_CORRUPT(如果变更集缓冲区已损坏)或SQLITE_NOMEM。


从变更集迭代器中获取old.*值

int sqlite3changeset_old(
  sqlite3_changeset_iter *pIter,  /* Changeset iterator */
  int iVal,                       /* Column number */
  sqlite3_value **ppValue         /* OUT: Old value (or NULL pointer) */
);

传递给此函数的pIter参数可以是传递给冲突处理程序的迭代器sqlite3changeset_apply(),也可以是sqlite3changeset_start()创建的迭代器。在后一种情况下,对sqlite3changeset_next()的最近一次调用必须返回SQLITE_ROW。此外,只能在迭代器当前指向的更改类型为SQLITE_DELETESQLITE_UPDATE时调用它。否则,此函数返回SQLITE_MISUSE并将*ppValue设置为NULL。

参数iVal必须大于或等于0,且小于受当前更改影响的表中的列数。否则,返回SQLITE_RANGE,并将*ppValue设置为NULL。

如果成功,则此函数将*ppValue设置为指向受保护的sqlite3_value对象,该对象包含存储为UPDATE或DELETE更改的一部分的原始行值向量中的第iVal个值,并返回SQLITE_OK。函数的名称来自这样一个事实,即这类似于更新或删除触发器可用的“old.*”列。

如果发生其他错误(例如OOM条件),则返回SQLite错误代码,并将*ppValue设置为NULL。


从变更集迭代器中获取当前操作

int sqlite3changeset_op(
  sqlite3_changeset_iter *pIter,  /* Iterator object */
  const char **pzTab,             /* OUT: Pointer to table name */
  int *pnCol,                     /* OUT: Number of columns in table */
  int *pOp,                       /* OUT: SQLITE_INSERT, DELETE or UPDATE */
  int *pbIndirect                 /* OUT: True for an 'indirect' change */
);

传递给此函数的pIter参数可以是传递给冲突处理程序的迭代器sqlite3changeset_apply(),也可以是sqlite3changeset_start()创建的迭代器。在后一种情况下,对sqlite3changeset_next()的最近一次调用必须返回SQLITE_ROW。如果不是这种情况,则此函数返回SQLITE_MISUSE

参数 pOp、pnCol 和 pzTab 不能为 NULL。返回后,通过这些指针设置三个输出。

*pOp 设置为 SQLITE_INSERTSQLITE_DELETESQLITE_UPDATE 之一,具体取决于迭代器当前指向的更改类型;

*pnCol 设置为受更改影响的表中的列数;以及

*pzTab 设置为指向一个以 null 结尾的 utf-8 编码字符串,该字符串包含受当前更改影响的表名。该缓冲区保持有效,直到对迭代器调用 sqlite3changeset_next() 或冲突处理程序函数返回。

如果 pbIndirect 不为 NULL,则如果更改是间接更改,则 *pbIndirect 设置为 true (1),否则设置为 false (0)。有关直接和间接更改的描述,请参阅 sqlite3session_indirect() 的文档。

如果未发生错误,则返回 SQLITE_OK。如果发生错误,则返回 SQLite 错误代码。在这种情况下,可能无法信任输出变量的值。


获取表的 PRIMARY KEY 定义

int sqlite3changeset_pk(
  sqlite3_changeset_iter *pIter,  /* Iterator object */
  unsigned char **pabPK,          /* OUT: Array of boolean - true for PK cols */
  int *pnCol                      /* OUT: Number of entries in output array */
);

对于每个修改的表,changeset 包含以下内容

此函数用于查找构成迭代器 pIter 当前指向的更改所修改的表的 PRIMARY KEY 的列。如果成功,则 *pabPK 将设置为指向一个包含 nCol 个条目的数组,其中 nCol 是表中的列数。如果相应的列是表主键的一部分,则 *pabPK 的元素设置为 0x01,否则设置为 0x00。

如果参数 pnCol 不为 NULL,则 *pnCol 设置为表中的列数。

如果在迭代器没有指向有效条目时调用此函数,则返回 SQLITE_MISUSE 并将输出变量清零。否则,返回 SQLITE_OK 并按上述说明填充输出变量。


升级 Changeset/Patchset 的 Schema

int sqlite3changeset_upgrade(
  sqlite3 *db,
  const char *zDb,
  int nIn, const void *pIn,       /* Input changeset */
  int *pnOut, void **ppOut        /* OUT: Inverse of input */
);


配置 changeset rebaser 对象。

int sqlite3rebaser_configure(
  sqlite3_rebaser*, 
  int nRebase, const void *pRebase
); 

重要提示:此接口是实验性的,如有更改,恕不另行通知。

配置 changeset rebaser 对象,以根据缓冲区 pRebase(大小为 nRebase 字节)中描述的冲突解决来重新设置 changeset 的基准,该缓冲区必须是从先前对 sqlite3changeset_apply_v2() 的调用中获得的。


创建一个 changeset rebaser 对象。

int sqlite3rebaser_create(sqlite3_rebaser **ppNew);

重要提示:此接口是实验性的,如有更改,恕不另行通知。

分配一个新的 changeset rebaser 对象。如果成功,则将 (*ppNew) 设置为指向新对象并返回 SQLITE_OK。否则,如果发生错误,则返回 SQLite 错误代码(例如 SQLITE_NOMEM)并将 (*ppNew) 设置为 NULL。


删除 changeset rebaser 对象。

void sqlite3rebaser_delete(sqlite3_rebaser *p); 

重要提示:此接口是实验性的,如有更改,恕不另行通知。

删除 changeset rebaser 对象和所有关联的资源。对于每次成功调用 sqlite3rebaser_create(),都应该调用此函数一次。


重新设置 changeset 的基准

int sqlite3rebaser_rebase(
  sqlite3_rebaser*,
  int nIn, const void *pIn, 
  int *pnOut, void **ppOut 
);

重要提示:此接口是实验性的,如有更改,恕不另行通知。

参数 pIn 必须指向一个包含大小为 nIn 字节的 changeset 的缓冲区。此函数分配并填充一个缓冲区,其中包含根据作为第一个参数传递的 rebaser 对象的配置重新设置基准的 changeset 的副本。如果成功,则 (*ppOut) 将设置为指向包含重新设置基准的 changeset 的新缓冲区,(*pnOut) 设置为其大小(以字节为单位),并返回 SQLITE_OK。调用者有责任最终使用 sqlite3_free() 释放新缓冲区。否则,如果发生错误,则 (*ppOut) 和 (*pnOut) 设置为零并返回 SQLite 错误代码。


将表附加到 Session 对象

int sqlite3session_attach(
  sqlite3_session *pSession,      /* Session object */
  const char *zTab                /* Table name */
);

如果参数 zTab 不为 NULL,则它是要附加到作为第一个参数传递的 session 对象的表的名称。在 session 对象启用期间对表进行的所有后续更改都将被记录。有关更多详细信息,请参阅 sqlite3session_changeset() 的文档。

或者,如果参数 zTab 为 NULL,则记录数据库中所有表的更改。如果在此调用之后向数据库添加了其他表(通过执行“CREATE TABLE”语句),则也会记录新表的更改。

仅可以为在 CREATE TABLE 语句中明确定义了 PRIMARY KEY 的表记录更改。PRIMARY KEY 是否为“INTEGER PRIMARY KEY”(rowid 别名)无关紧要。PRIMARY KEY 可以由单个列组成,也可以是复合键。

如果数据库中不存在指定的表,则不会发生错误。如果指定的表没有 PRIMARY KEY,也不会发生错误。但是,在这两种情况下都不会记录任何更改。

不会为在一个或多个 PRIMARY KEY 列中存储了 NULL 值的单个行记录更改。

如果调用在没有错误的情况下完成,则返回 SQLITE_OK。或者,如果发生错误,则返回 SQLite 错误代码(例如 SQLITE_NOMEM)。

特殊的 sqlite_stat1 处理

从 SQLite 3.22.0 版本开始,“sqlite_stat1”表是上述某些规则的例外。在 SQLite 中,sqlite_stat1 的模式为

       CREATE TABLE sqlite_stat1(tbl,idx,stat)  
 

即使 sqlite_stat1 没有 PRIMARY KEY,也会为其记录更改,就像 PRIMARY KEY 为 (tbl,idx) 一样。此外,还会为 (idx IS NULL) 为真的行记录更改。但是,对于此类行,会在 changeset 或 patchset 中存储零长度 blob(SQL 值 X''),而不是 NULL 值。这允许此类 changeset 由 sqlite3changeset_invert()、concat() 等的旧版实现进行操作。

sqlite3changeset_apply() 函数在更新 sqlite_stat1 表时会自动将零长度 blob 转换回 NULL 值。但是,如果应用程序直接调用 changeset 迭代器的 sqlite3changeset_new()、sqlite3changeset_old() 或 sqlite3changeset_conflict(包括传递给冲突处理程序回调的 changeset 迭代器),则会返回 X'' 值。如果需要,应用程序必须自行将 X'' 转换为 NULL。

旧版(早于 3.22.0)的 sessions 模块无法捕获对 sqlite_stat1 表所做的更改。旧版 sqlite3changeset_apply() 函数会静默忽略作为 changeset 或 patchset 一部分的对 sqlite_stat1 表的任何修改。


从 Session 对象生成 Changeset

int sqlite3session_changeset(
  sqlite3_session *pSession,      /* Session object */
  int *pnChangeset,               /* OUT: Size of buffer at *ppChangeset */
  void **ppChangeset              /* OUT: Buffer containing changeset */
);

获取一个包含对附加到作为第一个参数传递的 session 对象的表的更改的 changeset。如果成功,则在返回 SQLITE_OK 之前,将 *ppChangeset 设置为指向包含 changeset 的缓冲区,并将 *pnChangeset 设置为 changeset 的大小(以字节为单位)。如果发生错误,则将 *ppChangeset 和 *pnChangeset 都设置为零并返回 SQLite 错误代码。

Changeset 由零个或多个 INSERT、UPDATE 和/或 DELETE 更改组成,每个更改都表示对附加表的单行的更改。INSERT 更改包含新数据库行中每个字段的值。DELETE 包含已删除数据库行中每个字段的原始值。UPDATE 更改包含已更新数据库行中每个字段的原始值以及每个已更新的非主键列的更新值。UPDATE 更改无法表示修改主键列值的更改。如果进行了此类更改,则在 changeset 中表示为 DELETE 后跟 INSERT。

不会为在一个或多个 PRIMARY KEY 列中存储了 NULL 值的行记录更改。如果插入或删除了此类行,则 changeset 中不会存在相应的更改。如果将现有行(在一个或多个 PRIMARY KEY 列中存储了 NULL 值)更新为所有 PRIMARY KEY 列都不为 NULL,则仅在 changeset 中出现 INSERT。类似地,如果将现有行(具有非 NULL PRIMARY KEY 值)更新为一个或多个 PRIMARY KEY 列设置为 NULL,则生成的 changeset 仅包含 DELETE 更改。

可以使用使用 sqlite3changeset_start() API 创建的迭代器遍历 changeset 的内容。可以使用 sqlite3changeset_apply() API 将 changeset 应用于具有兼容模式的数据库。

在由此函数生成的 changeset 中,与单个表相关的更改将分组在一起。换句话说,当遍历 changeset 或将 changeset 应用于数据库时,在继续处理下一个表之前,会先处理与单个表相关的更改。表按附加(或自动附加)到 sqlite3_session 对象的相同顺序排序。与单个表相关的更改的存储顺序未定义。

在成功调用此函数后,调用者有责任最终使用 sqlite3_free() 释放 *ppChangeset 指向的缓冲区。

Changeset 生成

将表附加到 session 对象后,session 对象会记录插入到表中的所有新行的主键值。它还会记录任何已删除或更新行的原始主键和其他列值。对于每个唯一的主键值,仅记录一次数据 - 在 session 的生命周期中,第一次插入、更新或删除具有该主键的行时记录数据。

上一段话有一个例外:当插入、更新或删除行时,如果其一个或多个主键列包含 NULL 值,则不会记录更改。

因此,session 对象会累积两种类型的记录 - 仅由主键值组成的记录(在用户插入新记录时创建)和由主键值和其他表列的原始值组成的记录(在用户删除或更新记录时创建)。

当调用此函数时,将使用累积的记录和数据库文件的当前内容创建请求的 changeset。具体来说

这意味着,除其他事项外,如果在 session 对象处于活动状态时插入一行然后稍后删除该行,则 changeset 中既不会出现插入也不会出现删除。或者,如果在 session 对象处于活动状态时删除一行,然后稍后插入具有相同主键值的另一行,则生成的 changeset 将包含 UPDATE 更改,而不是 DELETE 和 INSERT。

当会话对象被禁用(参见 sqlite3session_enable() API)时,在插入、更新或删除行时不会累积记录。如果在会话期间多次写入同一行,这可能会产生一些违反直觉的效果。例如,如果在会话对象启用时插入一行,然后在同一会话对象禁用时删除该行,则更改集中不会出现任何 INSERT 记录,即使删除发生在会话禁用时也是如此。或者,如果在会话禁用时更新行的某个字段,而在会话启用时更新同一行的另一个字段,则生成的更改集将包含一个更新这两个字段的 UPDATE 更改。


返回更改集大小的上限

sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);

默认情况下,此函数始终返回 0。为了使其返回有用的结果,必须使用 sqlite3session_object_config() 函数(使用 SQLITE_SESSION_OBJCONFIG_SIZE 参数)配置 sqlite3_session 对象以启用此 API。

启用后,此函数返回更改集大小的上限(以字节为单位),该更改集可能是调用 sqlite3session_changeset() 时产生的。最终的更改集大小可能等于或小于此函数返回的字节大小。


配置全局参数

int sqlite3session_config(int op, void *pArg);

sqlite3session_config() 接口用于对会话模块进行全局配置更改,以便根据应用程序的特定需求对其进行调整。

sqlite3session_config() 接口不是线程安全的。如果在任何其他线程位于任何其他会话方法内部时调用它,则结果未定义。此外,如果在创建任何与会话相关的对象后调用它,结果也未定义。

sqlite3session_config() 函数的第一个参数必须是下面定义的 SQLITE_SESSION_CONFIG_XXX 常量之一。作为第二个参数传递的 (void*) 值的解释以及调用此函数的效果取决于第一个参数的值。

SQLITE_SESSION_CONFIG_STRMSIZE
默认情况下,会话模块流接口尝试以大约 1 KiB 的块输入和输出数据。此操作数可用于设置和查询此配置设置的值。作为第二个参数传递的指针必须指向类型为 (int) 的值。如果此值大于 0,则将其用作输入和输出的新流数据块大小。在返回之前,pArg 指向的 (int) 值将设置为流接口块大小的最终值。

如果成功,此函数返回 SQLITE_OK,否则返回 SQLite 错误代码。


创建一个新的会话对象

int sqlite3session_create(
  sqlite3 *db,                    /* Database handle */
  const char *zDb,                /* Name of db (e.g. "main") */
  sqlite3_session **ppSession     /* OUT: New session object */
);

创建一个附加到数据库句柄 db 的新会话对象。如果成功,则将指向新对象的指针写入 *ppSession 并返回 SQLITE_OK。如果发生错误,则 *ppSession 设置为 NULL 并返回 SQLite 错误代码(例如 SQLITE_NOMEM)。

可以创建多个附加到单个数据库句柄的会话对象。

使用此函数创建的会话对象应在附加到的数据库句柄本身关闭之前使用 sqlite3session_delete() 函数删除。如果在删除会话对象之前关闭了数据库句柄,则调用任何会话模块函数(包括在会话对象上调用 sqlite3session_delete())的结果未定义。

由于会话模块使用 sqlite3_preupdate_hook() API,因此应用程序无法在附加了一个或多个会话对象的数据库句柄上注册预更新钩子。同样,也无法为已定义预更新钩子的数据库句柄创建附加的会话对象。尝试执行这些操作中的任何一项的结果未定义。

会话对象将用于为数据库 zDb 中的表创建更改集,其中 zDb 为“main”或“temp”,或附加数据库的名称。如果在创建会话对象时数据库 zDb 未附加到数据库,则不会出错。


删除会话对象

void sqlite3session_delete(sqlite3_session *pSession);

删除之前使用 sqlite3session_create() 分配的会话对象。一旦会话对象被删除,尝试使用 pSession 与任何其他会话模块函数的结果将未定义。

必须在关闭附加到的数据库句柄之前删除会话对象。有关详细信息,请参阅 sqlite3session_create() 的文档。


将表之间的差异加载到会话中

int sqlite3session_diff(
  sqlite3_session *pSession,
  const char *zFromDb,
  const char *zTbl,
  char **pzErrMsg
);

如果尚未附加到作为第一个参数传递的会话对象,则此函数将以与 sqlite3session_attach() 函数相同的方式附加表 zTbl。如果 zTbl 不存在,或者没有主键,则此函数为无操作(但不返回错误)。

参数 zFromDb 必须是附加到与包含与由此函数附加到会话的表兼容的表的会话对象相同的数据库句柄的数据库(“main”,“temp”等)的名称。如果表满足以下条件,则被视为兼容

如果表不兼容,则返回 SQLITE_SCHEMA。如果表兼容但没有任何 PRIMARY KEY 列,则不会出错,但不会向会话对象添加任何更改。与其他会话 API 一样,没有 PRIMARY KEY 的表将被简单地忽略。

此函数向会话对象添加一组更改,这些更改可用于更新数据库 zFrom 中的表(称为“from-table”),使其内容与附加到会话对象的表(称为“to-table”)相同。具体来说

澄清一下,如果调用此函数然后使用 sqlite3session_changeset() 构造更改集,则在将该更改集应用于数据库 zFrom 后,两个兼容表的内容将相同。

如果数据库 zFrom 不存在或不包含所需的兼容表,则为错误。

如果操作成功,则返回 SQLITE_OK。否则,返回 SQLite 错误代码。在这种情况下,如果参数 pzErrMsg 不为 NULL,则 *pzErrMsg 可能设置为指向包含英文错误消息的缓冲区的指针。调用者有责任使用 sqlite3_free() 释放此缓冲区。


启用或禁用会话对象

int sqlite3session_enable(sqlite3_session *pSession, int bEnable);

启用或禁用会话对象记录更改。启用时,会话对象会记录对数据库所做的更改。禁用时,它不会记录更改。新创建的会话对象已启用。有关启用和禁用会话对象如何影响最终更改集的更多详细信息,请参阅 sqlite3session_changeset() 的文档。

将零传递给此函数会禁用会话。传递大于零的值会启用它。传递小于零的值是无操作,可用于查询会话的当前状态。

返回值指示会话对象的最终状态:如果会话已禁用,则为 0;如果已启用,则为 1。


设置或清除间接更改标志

int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);

会话对象记录的每个更改都标记为直接或间接。如果满足以下任一条件,则更改将标记为间接

如果同一行在会话内受到多个操作的影响,则如果所有操作都满足上述间接更改的条件,则该更改被视为间接更改,否则被视为直接更改。

此函数用于设置、清除或查询会话对象的间接标志。如果传递给此函数的第二个参数为零,则清除间接标志。如果大于零,则设置间接标志。传递小于零的值不会修改间接标志的当前值,可用于查询指定会话对象的间接标志的当前状态。

返回值指示间接标志的最终状态:如果已清除,则为 0;如果已设置,则为 1。


测试更改集是否记录了任何更改。

int sqlite3session_isempty(sqlite3_session *pSession);

如果作为第一个参数传递的会话对象未记录对附加表的任何更改,则返回非零值。否则,如果记录了一个或多个更改,则返回零。

即使此函数返回零,调用会话句柄上的 sqlite3session_changeset() 仍可能返回不包含任何更改的更改集。当附加表中的一行被修改,然后恢复其原始值时,就会发生这种情况。但是,如果此函数返回非零值,则保证对 sqlite3session_changeset() 的调用将返回包含零个更改的更改集。


查询会话对象使用的堆内存量。

sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);

此 API 返回作为唯一参数传递的会话对象当前使用的堆内存总量(以字节为单位)。


配置会话对象

int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);

此方法用于在创建会话对象后配置它。目前,第二个参数的唯一有效值为 SQLITE_SESSION_OBJCONFIG_SIZESQLITE_SESSION_OBJCONFIG_ROWID


从会话对象生成补丁集

int sqlite3session_patchset(
  sqlite3_session *pSession,      /* Session object */
  int *pnPatchset,                /* OUT: Size of buffer at *ppPatchset */
  void **ppPatchset               /* OUT: Buffer containing patchset */
);

补丁集和更改集之间的区别在于

补丁集 blob 可与所有 sqlite3changeset_xxx API 函数(除了 sqlite3changeset_invert())的最新版本一起使用,如果传递给它的是补丁集,则 sqlite3changeset_invert() 将返回 SQLITE_CORRUPT。类似地,尝试将补丁集 blob 与旧版本的 sqlite3changeset_xxx API 一起使用也会引发 SQLITE_CORRUPT 错误。

由于非主键“old.*”字段被省略,因此如果将补丁集传递给 sqlite3changeset_apply() API,则无法检测或报告任何 SQLITE_CHANGESET_DATA 冲突。其他冲突类型的工作方式与更改集相同。

补丁集中的更改按与 sqlite3session_changeset() 函数生成的更改集相同的方式排序(即,单个表的所有更改都分组在一起,表按附加到会话对象的顺序显示)。


在会话对象上设置表过滤器。

void sqlite3session_table_filter(
  sqlite3_session *pSession,      /* Session object */
  int(*xFilter)(
    void *pCtx,                   /* Copy of third arg to _filter_table() */
    const char *zTab              /* Table name */
  ),
  void *pCtx                      /* First argument passed to xFilter */
);

第二个参数 (xFilter) 是“过滤器回调”。对于未附加到会话对象的表中行的更改,将调用过滤器以确定是否应跟踪对该表行的更改。如果 xFilter 返回 0,则不跟踪更改。请注意,一旦附加了表,就不会再次调用 xFilter。


sqlite3changeset_apply_v2 的标志

#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT   0x0001
#define SQLITE_CHANGESETAPPLY_INVERT        0x0002
#define SQLITE_CHANGESETAPPLY_IGNORENOOP    0x0004
#define SQLITE_CHANGESETAPPLY_FKNOACTION    0x0008

以下标志可以通过第 9 个参数传递给 sqlite3changeset_apply_v2sqlite3changeset_apply_v2_strm

SQLITE_CHANGESETAPPLY_NOSAVEPOINT
通常,sessions 模块会将通过单次调用 apply_v2() 或 apply_v2_strm() 执行的所有操作封装在一个SAVEPOINT 中。如果成功应用了更改集或补丁集,则提交 SAVEPOINT;如果发生错误,则回滚 SAVEPOINT。指定此标志会导致 sessions 模块省略此 savepoint。在这种情况下,如果调用者在调用 apply_v2() 时存在打开的事务或 savepoint,它可能会通过回滚来撤消部分应用的更改集。

SQLITE_CHANGESETAPPLY_INVERT
在应用更改集之前对其进行反转。这等效于在应用更改集之前使用 sqlite3changeset_invert() 对其进行反转。为补丁集指定此标志是错误的。

SQLITE_CHANGESETAPPLY_IGNORENOOP
对于即使应用也不会实际修改数据库的任何更改,都不要调用冲突处理程序回调。具体来说,这意味着冲突处理程序不会针对以下情况调用:
  • 如果找不到要删除的行,则删除更改;
  • 如果已将修改的字段设置为冲突行中的新值,则更新更改;或
  • 如果冲突行的所有字段都与要插入的行匹配,则插入更改。

SQLITE_CHANGESETAPPLY_FKNOACTION
如果设置了此标志,则目标数据库中的所有外键约束的行为都好像它们是用“ON UPDATE NO ACTION ON DELETE NO ACTION”声明的一样,即使它们实际上是 CASCADE、RESTRICT、SET NULL 或 SET DEFAULT。


冲突处理程序返回的常量

#define SQLITE_CHANGESET_OMIT       0
#define SQLITE_CHANGESET_REPLACE    1
#define SQLITE_CHANGESET_ABORT      2

冲突处理程序回调必须返回以下三个值之一。

SQLITE_CHANGESET_OMIT
如果冲突处理程序返回此值,则不会采取任何特殊操作。导致冲突的更改不会应用。sessions 模块继续执行更改集中的下一个更改。

SQLITE_CHANGESET_REPLACE
仅当冲突处理程序的第二个参数为 SQLITE_CHANGESET_DATA 或 SQLITE_CHANGESET_CONFLICT 时,才能返回此值。如果不是这种情况,则回滚到目前为止应用的所有更改,并且对 sqlite3changeset_apply() 的调用将返回 SQLITE_MISUSE。

如果 SQLITE_CHANGESET_DATA 冲突处理程序返回 CHANGESET_REPLACE,则根据更改的类型更新或删除冲突行。

如果 SQLITE_CHANGESET_CONFLICT 冲突处理程序返回 CHANGESET_REPLACE,则从数据库中删除冲突行并再次尝试应用更改。如果第二次尝试失败,则在继续之前将原始行恢复到数据库中。

SQLITE_CHANGESET_ABORT
如果返回此值,则回滚到目前为止应用的所有更改,并且对 sqlite3changeset_apply() 的调用将返回 SQLITE_ABORT。


传递给冲突处理程序的常量

#define SQLITE_CHANGESET_DATA        1
#define SQLITE_CHANGESET_NOTFOUND    2
#define SQLITE_CHANGESET_CONFLICT    3
#define SQLITE_CHANGESET_CONSTRAINT  4
#define SQLITE_CHANGESET_FOREIGN_KEY 5

可以作为冲突处理程序的第二个参数传递的值。

SQLITE_CHANGESET_DATA
在处理 DELETE 或 UPDATE 更改时,如果数据库中存在具有所需 PRIMARY KEY 字段的行,但更新修改的一个或多个其他(非主键)字段不包含预期的“before”值,则冲突处理程序将使用 CHANGESET_DATA 作为第二个参数被调用。

在这种情况下,冲突行是具有匹配主键的数据库行。

SQLITE_CHANGESET_NOTFOUND
在处理 DELETE 或 UPDATE 更改时,如果数据库中不存在具有所需 PRIMARY KEY 字段的行,则冲突处理程序将使用 CHANGESET_NOTFOUND 作为第二个参数被调用。

在这种情况下,没有冲突行。调用 sqlite3changeset_conflict() API 的结果未定义。

SQLITE_CHANGESET_CONFLICT
在处理 INSERT 更改时,如果操作会导致重复的主键值,则将 CHANGESET_CONFLICT 作为第二个参数传递给冲突处理程序。

在这种情况下,冲突行是具有匹配主键的数据库行。

SQLITE_CHANGESET_FOREIGN_KEY
如果启用了外键处理,并且应用更改集后使数据库处于包含外键违规的状态,则在提交更改集之前,冲突处理程序将使用 CHANGESET_FOREIGN_KEY 作为第二个参数被精确调用一次。如果冲突处理程序返回 CHANGESET_OMIT,则提交更改(包括导致外键约束违规的更改)。或者,如果它返回 CHANGESET_ABORT,则回滚更改集。

不提供当前行或冲突行信息。在提供的 sqlite3_changeset_iter 处理程序上唯一可以调用的函数是 sqlite3changeset_fk_conflicts()。

SQLITE_CHANGESET_CONSTRAINT
如果在应用更改时发生任何其他约束违规(即 UNIQUE、CHECK 或 NOT NULL 约束),则冲突处理程序将使用 CHANGESET_CONSTRAINT 作为第二个参数被调用。

在这种情况下,没有冲突行。调用 sqlite3changeset_conflict() API 的结果未定义。


sqlite3session_object_config 的选项

#define SQLITE_SESSION_OBJCONFIG_SIZE  1
#define SQLITE_SESSION_OBJCONFIG_ROWID 2

以下值可以作为 sqlite3session_object_config() 的第二个参数传递。

SQLITE_SESSION_OBJCONFIG_SIZE
此选项用于设置、清除或查询启用 sqlite3session_changeset_size() API 的标志。由于它会带来一些计算开销,因此默认情况下禁用此 API。参数 pArg 必须指向类型为 (int) 的值。如果该值最初为 0,则禁用 sqlite3session_changeset_size() API。如果它大于 0,则启用相同的 API。或者,如果初始值小于零,则不进行任何更改。在所有情况下,如果在当前调用后启用了 sqlite3session_changeset_size() API,则 (int) 变量将设置为 1,否则设置为 0。

在将第一个表附加到 session 对象之后尝试修改此设置是错误的 (SQLITE_MISUSE)。

SQLITE_SESSION_OBJCONFIG_ROWID
此选项用于设置、清除或查询启用为没有显式 PRIMARY KEY 的表收集数据的标志。

通常,sessions 模块会简单地忽略没有显式 PRIMARY KEY 的表。但是,如果设置了此标志,则其行为就像在这些表的左侧列插入了一个“_rowid_ INTEGER PRIMARY KEY”列一样。

在将第一个表附加到 session 对象之后尝试修改此设置是错误的 (SQLITE_MISUSE)。


API 函数的流式版本。

int sqlite3changeset_apply_strm(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
  void *pIn,                                          /* First arg for xInput */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx                      /* First argument passed to xConflict */
);
int sqlite3changeset_apply_v2_strm(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
  void *pIn,                                          /* First arg for xInput */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase,
  int flags
);
int sqlite3changeset_concat_strm(
  int (*xInputA)(void *pIn, void *pData, int *pnData),
  void *pInA,
  int (*xInputB)(void *pIn, void *pData, int *pnData),
  void *pInB,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);
int sqlite3changeset_invert_strm(
  int (*xInput)(void *pIn, void *pData, int *pnData),
  void *pIn,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);
int sqlite3changeset_start_strm(
  sqlite3_changeset_iter **pp,
  int (*xInput)(void *pIn, void *pData, int *pnData),
  void *pIn
);
int sqlite3changeset_start_v2_strm(
  sqlite3_changeset_iter **pp,
  int (*xInput)(void *pIn, void *pData, int *pnData),
  void *pIn,
  int flags
);
int sqlite3session_changeset_strm(
  sqlite3_session *pSession,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);
int sqlite3session_patchset_strm(
  sqlite3_session *pSession,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);
int sqlite3changegroup_add_strm(sqlite3_changegroup*, 
    int (*xInput)(void *pIn, void *pData, int *pnData),
    void *pIn
);
int sqlite3changegroup_output_strm(sqlite3_changegroup*,
    int (*xOutput)(void *pOut, const void *pData, int nData), 
    void *pOut
);
int sqlite3rebaser_rebase_strm(
  sqlite3_rebaser *pRebaser,
  int (*xInput)(void *pIn, void *pData, int *pnData),
  void *pIn,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);

六个流式 API xxx_strm() 函数的功能与相应的非流式 API 函数类似

流式函数非流式等效项
sqlite3changeset_apply_strmsqlite3changeset_apply
sqlite3changeset_apply_strm_v2sqlite3changeset_apply_v2
sqlite3changeset_concat_strmsqlite3changeset_concat
sqlite3changeset_invert_strmsqlite3changeset_invert
sqlite3changeset_start_strmsqlite3changeset_start
sqlite3session_changeset_strmsqlite3session_changeset
sqlite3session_patchset_strmsqlite3session_patchset

作为输入接受更改集(或补丁集)的非流式函数要求将整个更改集存储在内存中的单个缓冲区中。类似地,那些返回更改集或补丁集的函数通过返回指向使用 sqlite3_malloc() 分配的单个大型缓冲区的指针来实现。通常这很方便。但是,如果在低内存环境中运行的应用程序需要处理非常大的更改集,则所需的大型连续内存分配可能会变得繁重。

为了避免此问题,输入不是传递给单个大型缓冲区,而是通过 sessions 模块调用的回调函数传递给流式 API 函数,以便在需要时增量请求输入数据。在所有情况下,一对 API 函数参数,例如

       int nChangeset,
       void *pChangeset,
 

被替换为

       int (*xInput)(void *pIn, void *pData, int *pnData),
       void *pIn,
 

每次 sessions 模块调用 xInput 回调时,传递的第一个参数都是提供的 pIn 上下文指针的副本。第二个参数 pData 指向大小为 (*pnData) 字节的缓冲区。假设没有发生错误,xInput 方法应将最多 (*pnData) 字节的数据复制到缓冲区中,并在返回 SQLITE_OK 之前将 (*pnData) 设置为实际复制的字节数。如果输入完全用尽,则应将 (*pnData) 设置为零以指示这一点。或者,如果发生错误,则应返回 SQLite 错误代码。在所有情况下,如果 xInput 回调返回错误,则将放弃所有处理,并且流式 API 函数将错误代码的副本返回给调用方。

在 sqlite3changeset_start_strm() 的情况下,sessions 模块可以在迭代器生命周期的任何时间点调用 xInput 回调。如果此类 xInput 回调返回错误,则迭代器将进入错误状态,在此状态下,对迭代器函数的所有后续调用都将立即失败,并返回与 xInput 返回的相同的错误代码。

类似地,返回更改集(或补丁集)的流式 API 函数通过回调函数而不是通过指向单个大型缓冲区的指针分块返回它们。在这种情况下,一对参数,例如

       int *pnChangeset,
       void **ppChangeset,
 

被替换为

       int (*xOutput)(void *pOut, const void *pData, int nData),
       void *pOut
 

xOutput 回调被调用零次或多次以将数据返回到应用程序。传递给每个调用的第一个参数都是应用程序提供的 pOut 指针的副本。第二个参数 pData 指向大小为 nData 字节的缓冲区,其中包含正在返回的输出数据块。如果 xOutput 回调成功处理了提供的数据,则应返回 SQLITE_OK 以指示成功。否则,它应返回其他一些 SQLite 错误代码。在这种情况下,处理将立即被放弃,并且流式 API 函数将 xOutput 错误代码的副本返回到应用程序。

sessions 模块从不使用第三个参数设置为小于或等于零的值调用 xOutput 回调。除此之外,不对返回的数据块的大小做出任何保证。


将更改集应用到数据库

int sqlite3changeset_apply(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx                      /* First argument passed to xConflict */
);
int sqlite3changeset_apply_v2(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase, /* OUT: Rebase data */
  int flags                       /* SESSION_CHANGESETAPPLY_* flags */
);

将更改集或补丁集应用到数据库。这些函数尝试使用通过第二个和第三个参数传递的更改集中找到的更改来更新附加到句柄 db 的“main”数据库。

传递给这些函数的第四个参数 (xFilter) 是“过滤器回调”。如果它不为 NULL,则对于受更改集中至少一个更改影响的每个表,都会使用表名作为第二个参数调用过滤器回调,并使用作为第六个参数传递的上下文指针的副本作为第一个参数。如果“过滤器回调”返回零,则不会尝试将任何更改应用于该表。否则,如果返回值为非零或 xFilter 参数为 NULL,则将尝试所有与该表相关的更改。

对于每个未被过滤器回调排除的表,此函数都会测试目标数据库是否包含兼容的表。如果以下所有条件都为真,则表被认为是兼容的

  • 该表的名称与更改集中记录的名称相同,并且
  • 该表至少包含与更改集中记录的列一样多的列,并且
  • 该表的主键列位于与更改集中记录的相同位置。

如果没有兼容的表,则这不是错误,但不会应用与该表关联的任何更改。通过 sqlite3_log() 机制使用错误代码 SQLITE_SCHEMA 发出警告消息。对于更改集中的每个表,最多发出一个此类警告。

对于每个存在兼容表的更改,都会尝试根据 UPDATE、INSERT 或 DELETE 更改修改表内容。如果无法干净地应用更改,则可能会调用作为 sqlite3changeset_apply() 的第五个参数传递的冲突处理程序函数。下面详细说明了何时为每种类型的更改调用冲突处理程序。

与 xFilter 参数不同,xConflict 不能传递 NULL。将除有效函数指针以外的任何内容作为 xConflict 参数传递的结果未定义。

每次调用冲突处理函数时,都必须返回以下值之一:SQLITE_CHANGESET_OMITSQLITE_CHANGESET_ABORTSQLITE_CHANGESET_REPLACE。只有当传递给冲突处理函数的第二个参数为 SQLITE_CHANGESET_DATA 或 SQLITE_CHANGESET_CONFLICT 时,才能返回 SQLITE_CHANGESET_REPLACE。如果冲突处理函数返回非法值,则任何已进行的更改都会回滚,并且对 sqlite3changeset_apply() 的调用将返回 SQLITE_MISUSE。sqlite3changeset_apply() 会根据冲突处理函数每次调用的返回值采取不同的操作。有关详细信息,请参阅这三个可用返回值 的文档。

删除更改
对于每个 DELETE 更改,函数都会检查目标数据库中是否存在与更改集中存储的原始行值具有相同主键值(或值)的行。如果存在,并且所有非主键列中存储的值也与更改集中存储的值匹配,则该行将从目标数据库中删除。

如果找到具有匹配主键值的行,但一个或多个非主键字段包含的值与更改集中存储的原始行值不同,则将使用 SQLITE_CHANGESET_DATA 作为第二个参数调用冲突处理函数。如果数据库表包含比更改集中记录的更多的列,则仅将这些非主键字段的值与当前数据库内容进行比较 - 将忽略任何尾随的数据库表列。

如果在数据库中找不到具有匹配主键值的行,则将使用传递的 SQLITE_CHANGESET_NOTFOUND 作为第二个参数调用冲突处理函数。

如果尝试执行 DELETE 操作,但 SQLite 返回 SQLITE_CONSTRAINT(这只能在违反外键约束时发生),则将使用传递的 SQLITE_CHANGESET_CONSTRAINT 作为第二个参数调用冲突处理函数。这包括由于之前对冲突处理函数的调用返回 SQLITE_CHANGESET_REPLACE 而尝试执行 DELETE 操作的情况。

插入更改
对于每个 INSERT 更改,都会尝试将新行插入数据库。如果更改集行包含的字段少于数据库表,则尾随字段将填充其默认值。

如果插入行尝试失败,因为数据库中已存在具有相同主键值的行,则将使用设置为 SQLITE_CHANGESET_CONFLICT 的第二个参数调用冲突处理函数。

如果插入行尝试失败,因为其他一些约束违规(例如 NOT NULL 或 UNIQUE),则将使用设置为 SQLITE_CHANGESET_CONSTRAINT 的第二个参数调用冲突处理函数。这包括由于之前对冲突处理函数的调用返回 SQLITE_CHANGESET_REPLACE 而重新尝试 INSERT 操作的情况。

更新更改
对于每个 UPDATE 更改,函数都会检查目标数据库中是否存在与更改集中存储的原始行值具有相同主键值(或值)的行。如果存在,并且所有修改过的非主键列中存储的值也与更改集中存储的值匹配,则该行将在目标数据库中更新。

如果找到具有匹配主键值的行,但一个或多个修改过的非主键字段包含的值与更改集中存储的原始行值不同,则将使用 SQLITE_CHANGESET_DATA 作为第二个参数调用冲突处理函数。由于 UPDATE 更改仅包含要修改的非主键字段的值,因此只需要这些字段与原始值匹配即可避免 SQLITE_CHANGESET_DATA 冲突处理程序回调。

如果在数据库中找不到具有匹配主键值的行,则将使用传递的 SQLITE_CHANGESET_NOTFOUND 作为第二个参数调用冲突处理函数。

如果尝试执行 UPDATE 操作,但 SQLite 返回 SQLITE_CONSTRAINT,则将使用传递的 SQLITE_CHANGESET_CONSTRAINT 作为第二个参数调用冲突处理函数。这包括在之前对冲突处理函数的调用返回 SQLITE_CHANGESET_REPLACE 后尝试执行 UPDATE 操作的情况。

从 xConflict 回调内部安全地执行 SQL 语句,包括那些写入回调相关的表的语句。这可用于进一步自定义应用程序的冲突解决策略。

这些函数进行的所有更改都包含在一个保存点事务中。如果发生任何其他错误(除了尝试写入目标数据库时的约束失败),则保存点事务将回滚,将目标数据库恢复到其原始状态,并返回 SQLite 错误代码。

如果输出参数 (ppRebase) 和 (pnRebase) 不为 NULL,并且输入是更改集(而不是补丁集),则 sqlite3changeset_apply_v2() 可能会在返回之前将 (*ppRebase) 设置为指向一个“rebase”,该“rebase”可与 sqlite3_rebaser API 缓冲区一起使用。在这种情况下,(*pnRebase) 将设置为缓冲区的大小(以字节为单位)。调用方有责任最终使用 sqlite3_free() 释放任何此类缓冲区。仅当应用补丁集时遇到一个或多个冲突时,才会分配和填充缓冲区。有关更多详细信息,请参阅 sqlite3_rebaser API 周围的注释。

可以通过将 支持的标志 的组合作为第 9 个参数传递来修改 sqlite3changeset_apply_v2() 及其流等效项的行为。

请注意,sqlite3changeset_apply_v2() API 仍处于实验阶段,因此可能会发生更改。


创建迭代器以遍历更改集

int sqlite3changeset_start(
  sqlite3_changeset_iter **pp,    /* OUT: New changeset iterator handle */
  int nChangeset,                 /* Size of changeset blob in bytes */
  void *pChangeset                /* Pointer to blob containing changeset */
);
int sqlite3changeset_start_v2(
  sqlite3_changeset_iter **pp,    /* OUT: New changeset iterator handle */
  int nChangeset,                 /* Size of changeset blob in bytes */
  void *pChangeset,               /* Pointer to blob containing changeset */
  int flags                       /* SESSION_CHANGESETSTART_* flags */
);

创建一个用于迭代遍历更改集内容的迭代器。如果成功,则 *pp 将设置为指向迭代器句柄,并返回 SQLITE_OK。否则,如果发生错误,则 *pp 将设置为零,并返回 SQLite 错误代码。

可以使用以下函数来推进和查询由此函数创建的更改集迭代器

调用方有责任最终通过将其传递给 sqlite3changeset_finalize() 来销毁迭代器。包含更改集 (pChangeset) 的缓冲区必须在迭代器销毁后保持有效。

假设更改集 blob 是由 sqlite3session_changeset()sqlite3changeset_concat()sqlite3changeset_invert() 函数之一创建的,则更改集中应用于单个表的所有更改都将分组在一起。这意味着当应用程序使用由此函数创建的迭代器迭代遍历更改集时,将连续访问与单个表相关的所有更改。迭代器不可能访问应用于表 X 的更改,然后访问表 Y 的更改,然后稍后访问表 X 的另一个更改。

可以通过将 支持的标志 的组合作为第 4 个参数传递来修改 sqlite3changeset_start_v2() 及其流等效项的行为。

请注意,sqlite3changeset_start_v2() API 仍处于实验阶段,因此可能会发生更改。