Small. Fast. Reliable.
Choose any three.
SQLite的虚拟表机制

1.简介

虚拟表是在打开的SQLite数据库连接中注册的对象 。从SQL语句的角度来看,虚拟表对象看起来像任何其他表或视图。但是在后台,对虚拟表的查询和更新将调用虚拟表对象的回调方法,而不是对数据库文件进行读写。

虚拟表机制允许应用程序发布可从SQL语句访问的接口,就像它们是表一样。SQL语句几乎可以对虚拟表执行任何操作,而对虚拟表可以执行的操作除外,但以下情况除外:

各个虚拟表实现可能会施加其他约束。例如,某些虚拟实现可能提供只读表。或者某些虚拟表实现可能允许 INSERTDELETE,但不允许UPDATE。或者某些虚拟表实现可能会限制可以进行的UPDATE的种类。

虚拟表可能表示内存中的数据结构。或者它可能代表磁盘上非SQLite格式的数据视图。或者,应用程序可以按需计算虚拟表的内容。

这是虚拟表的一些现有的和假定的用法:

有关实际虚拟表实现的详细列表,请参见虚拟表列表页面。

1.1。用法

使用CREATE VIRTUAL TABLE语句创建虚拟表

创建虚拟表stmt:

CREATE VIRTUAL TABLE IF NOT EXISTS schema-name . table-name USING module-name ( module-argument ) ,

CREATE VIRTUAL TABLE语句创建一个名为table-name的新表,该是从类class module-name派生的。的模块名称 是由注册的虚拟表的名称sqlite3_create_module()接口。

使用模块名创建虚拟表表名;

还可以在模块名称后为模块提供逗号分隔的参数:

使用modulename(arg1,arg2,...)创建虚拟表表名

模块参数的格式非常通用。每个 模块参数 可以包含关键字,字符串文字,标识符,数字和标点符号。创建虚拟表时,每个模块参数都以书面形式(以文本形式)传递到虚拟表实现的 构造函数方法中,并且该构造函数负责解析和解释参数。参数语法足够通用,因此虚拟表实现可以在普通的CREATE TABLE语句中将其参数解释为列定义。该实现还可以对这些论点强加一些其他解释。

一旦创建了虚拟表,就可以像其他任何表一样使用该表,但上面指出的例外情况除外,并且由特定的虚拟表实现实施。使用普通的DROP TABLE语法销毁虚拟表 。

1.1.1。临时虚拟表

没有“ CREATE TEMP VIRTUAL TABLE”语句。要创建临时虚拟表,请在虚拟表名称之前添加“ temp”架构。

创建虚拟表温度 表名使用模块(arg1,...);

1.1.2。同名虚拟表

即使没有CREATE VIRTUAL TABLE语句,某些虚拟表也会自动存在于其模块已注册到的每个数据库连接的“主”模式中。这样的虚拟表被称为“同名虚拟表”。要使用同名的虚拟表,只需像使用表一样使用模块名称即可。同义的虚拟表仅存在于“主”模式中,因此如果以不同的模式名称作为前缀,则它们将不起作用。

dbstat虚拟表就是一个同名虚拟表的示例。要将dbstat虚拟表用作同义虚拟表,只需查询“ dbstat”模块名称,就好像它是普通表一样。(请注意,必须使用SQLITE_ENABLE_DBSTAT_VTAB选项编译SQLite才能在构建中包括dbstat虚拟表。)

选择* FROM dbstat;

如果虚拟表的xCreate方法的功能与xConnect方法的功能完全相同,或者xCreate方法的值为NULL ,则它是同义的。该xCreate当使用第一次创建一个虚拟表方法被调用创建虚拟TABLE语句。该xConnect方法被调用每当数据库连接附连到或重新解析的模式。当这两种方法相同时,表明虚拟表没有需要创建和销毁的持久状态。

1.1.3。仅同名的虚拟表

如果xCreate方法为NULL,则 该虚拟表禁止使用CREATE VIRTUAL TABLE语句,并且该虚拟表是“仅具有别名的虚拟表”。仅具有名称的虚拟表可用作 表值函数

请注意,在版本3.9.0(2015-10-14)之前,SQLite在调用xCreate方法之前未检查其是否为NULL。因此,如果在SQLite版本3.8.11.1(2015-07-29)或更早版本中注册了仅具名的 虚拟表,并且对该虚拟表模块尝试了CREATE VIRTUAL TABLE命令,则会发生跳转到NULL指针的情况,从而导致崩溃。

1.2。执行

虚拟表实现使用了几个新的C级对象:

typedef struct sqlite3_vtab sqlite3_vtab;
typedef struct sqlite3_index_info sqlite3_index_info;
typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor;
typedef struct sqlite3_module sqlite3_module;

sqlite3_module结构定义了用于实现虚拟表的模块对象。将模块视为一类,从中可以构造具有相似属性的多个虚拟表。例如,可能有一个模块可以提供对磁盘上逗号分隔值(CSV)文件的只读访问。然后,可以使用该模块创建多个虚拟表,其中每个虚拟表都引用一个不同的CSV文件。

模块结构包含由SQLite调用以对虚拟表执行各种操作的方法,例如创建虚拟表的新实例或销毁旧实例,读取和写入数据,搜索和删除,更新或插入行。模块结构将在下面更详细地说明。

每个虚拟表实例均由sqlite3_vtab结构表示。sqlite3_vtab结构如下所示:

struct sqlite3_vtab {
  const sqlite3_module * pModule;
  int nRef;
  char * zErrMsg;
};

虚拟表实现通常将对该结构进行子类化,以添加其他私有字段和特定于实现的字段。nRef字段由SQLite内核在内部使用,不应由虚拟表实现更改。通过将错误消息字符串放入zErrMsg,虚拟表实现可以将错误消息文本传递给核心。必须从SQLite内存分配函数(例如sqlite3_mprintf()sqlite3_malloc())获取用于保存此错误消息字符串的空间。在为zErrMsg分配新值之前,虚拟表实现必须使用sqlite3_free()释放zErrMsg的任何先前存在的内容。 。否则会导致内存泄漏。当SQLite内核将错误消息文本发送到客户端应用程序或销毁虚拟表时,它将释放zErrMsg的内容并将其内容清零。当虚拟表实现用新的不同错误消息覆盖内容时,只需要担心释放zErrMsg内容。

sqlite3_vtab_cursor结构表示一个指向虚拟表的特定行。这是sqlite3_vtab_cursor的样子:

struct sqlite3_vtab_cursor {
  sqlite3_vtab * pVtab;
};

再一次,实际的实现可能会将该结构子类化,以添加其他私有字段。

sqlite3_index_info结构用于传递信息流入和流出所述模块实现的虚拟表的xBestIndex方法。

一个之前创建虚拟TABLE语句可以运行,在语句中指定的模块必须与数据库连接进行注册。使用sqlite3_create_module()sqlite3_create_module_v2()接口均可完成此操作:

int sqlite3_create_module(
  sqlite3 * db,/ * SQLite连接以* /来注册模块
  const char * zName,/ *模块名称* /
  const sqlite3_module *,/ *模块的方法* /
  无效* / * xCreate / xConnect的客户端数据* /
);
int sqlite3_create_module_v2(
  sqlite3 * db,/ * SQLite连接以* /来注册模块
  const char * zName,/ *模块名称* /
  const sqlite3_module *,/ *模块的方法* /
  无效*,/ * xCreate / xConnect的客户端数据* /
  void(* xDestroy)(void *)/ *客户端数据析构函数* /
);

所述sqlite3_create_module()sqlite3_create_module_v2() 例程相关联的模块名称与一个sqlite3_module结构和特定于每个模块的单独客户机数据。这两个create_module方法之间的唯一区别是_v2方法包括一个额外的参数,该参数指定客户端数据指针的析构函数。模块结构定义了虚拟表的行为。模块结构如下所示:

struct sqlite3_module {
  int iVersion;
  int(* xCreate)(sqlite3 *,无效* pAux,
               int argc,char * const * argv,
               sqlite3_vtab ** ppVTab,
               char ** pzErr);
  int(* xConnect)(sqlite3 *,无效* pAux,
               int argc,char * const * argv,
               sqlite3_vtab ** ppVTab,
               char ** pzErr);
  int(* xBestIndex)(sqlite3_vtab * pVTab,sqlite3_index_info *);
  int(* xDisconnect)(sqlite3_vtab * pVTab);
  int(* xDestroy)(sqlite3_vtab * pVTab);
  int(* xOpen)(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor);
  int(* xClose)(sqlite3_vtab_cursor *);
  int(* xFilter)(sqlite3_vtab_cursor *,int idxNum,const char * idxStr,
                int argc,sqlite3_value ** argv);
  int(* xNext)(sqlite3_vtab_cursor *);
  int(* xEof)(sqlite3_vtab_cursor *);
  int(* xColumn)(sqlite3_vtab_cursor *,sqlite3_context *,int);;
  int(* xRowid)(sqlite3_vtab_cursor *,sqlite_int64 * pRowid);
  int(* xUpdate)(sqlite3_vtab *,int,sqlite3_value **,sqlite_int64 *);
  int(* xBegin)(sqlite3_vtab * pVTab);
  int(* xSync)(sqlite3_vtab * pVTab);
  int(* xCommit)(sqlite3_vtab * pVTab);
  int(* xRollback)(sqlite3_vtab * pVTab);
  int(* xFindFunction)(sqlite3_vtab * pVtab,int nArg,const char * zName,
                     void(** pxFunc)(sqlite3_context *,int,sqlite3_value **),
                     void ** ppArg);
  int(*重命名)(sqlite3_vtab * pVtab,const char * zNew);
  / *上面的方法在sqlite_module对象的版本1中。那些
  以下**适用于版本2和更高版本。* /
  int(* xSavepoint)(sqlite3_vtab * pVTab,int);
  int(* xRelease)(sqlite3_vtab * pVTab,int);
  int(* xRollbackTo)(sqlite3_vtab * pVTab,int);
  / *上面的方法在sqlite_module对象的版本1和2中。
  **以下内容适用于版本3和更高版本。* /
  int(* xShadowName)(const char *);
};

模块结构为每个虚拟表对象定义了所有方法。模块结构还包含iVersion字段,该字段定义模块表结构的特定版本。当前,iVersion始终为3或更少,但是在SQLite的将来版本中,模块结构定义可能会使用其他方法进行扩展,在这种情况下,iVersion的最大值将增加。

模块结构的其余部分由用于实现虚拟表的各种功能的方法组成。有关这些方法的每种方法的详细信息,将在后续文章中提供。

1.3。虚拟表和共享缓存

在SQLite 3.6.17(2009-08-10)版本之前,虚拟表机制假定每个数据库连接都保留自己的数据库模式副本。因此,不能在启用了共享缓存模式的数据库中使用虚拟表机制。该sqlite3_create_module()如果接口将返回一个错误 共享缓存模式被启用。从SQLite 3.6.17版本开始放宽了该限制。

1.4。创建新的虚拟表实现

请按照以下步骤创建您自己的虚拟表:

  1. 编写所有必要的方法。
  2. 创建sqlite3_module结构的实例,该实例包含指向第1步中所有方法的指针。
  3. 使用sqlite3_create_module()sqlite3_create_module_v2()接口之一注册sqlite3_module结构 。
  4. 运行CREATE VIRTUAL TABLE命令,该命令在USING子句中指定新模块。

唯一真正困难的部分是步骤1。您可能想从现有的虚拟表实现开始,并对其进行修改以适合您的需求。的SQLite的源树 中包含许多虚拟表实现适合于复制,包括:

SQLite源代码树中还有许多其他虚拟表实现可以用作示例。通过搜索“ sqlite3_create_module”找到这些其他虚拟表实现。

您可能还希望将新的虚拟表实现为 可加载扩展

2.虚拟表方法

2.1。xCreate方法

int(* xCreate)(sqlite3 * db,void * pAux,
             int argc,char * const * argv,
             sqlite3_vtab ** ppVTab,
             char ** pzErr);

响应于CREATE VIRTUAL TABLE语句,调用xCreate方法来创建虚拟表的新实例。如果xCreate方法是相同的指针作为xConnect方法,则虚拟表是一个同名虚拟表。如果省略xCreate方法(如果它是NULL指针),则虚拟表是具有别名的虚拟表

db参数是执行CREATE VIRTUAL TABLE语句的SQLite数据库连接的指针。pAux参数是客户端数据指针的副本,该副本是已注册虚拟表模块sqlite3_create_module()sqlite3_create_module_v2()调用 的第四个参数。argv参数是指向空终止字符串的argc指针的数组。第一个字符串argv [0]是所调用模块的名称。模块名称是作为sqlite3_create_module()的第二个参数以及CREATE VIRTUAL TABLE的USING子句的参数 提供的名称。 正在运行的语句。第二个参数argv [1]是在其中创建新虚拟表的数据库的名称。对于主数据库,数据库名称为“ main”,对于TEMP数据库,数据库名称为“ temp”, 对于连接的数据库,数据库名称为ATTACH语句末尾给出的名称。数组的第三个元素argv [2]是新虚拟表的名称,该新虚拟表的名称在CREATE VIRTUAL TABLE语句中的TABLE关键字之后指定。如果存在,则argv []数组中的第四个字符串和后续字符串将在CREATE VIRTUAL TABLE语句中向模块名称报告参数。

此方法的工作是构造新的虚拟表对象(sqlite3_vtab对象),并在* ppVTab中返回指向它的指针。

作为创建新的sqlite3_vtab结构的任务的一部分,此方法必须调用sqlite3_declare_vtab()来告知SQLite核心有关虚拟表中的列和数据类型。该sqlite3_declare_vtab() API的原型如下:

int sqlite3_declare_vtab(sqlite3 * db,const char * zCreateTable)

的第一个参数sqlite3_declare_vtab()必须是相同的 数据库连接的指针作为第一个参数,以这种方法。sqlite3_declare_vtab()的第二个参数必须为零结尾的UTF-8字符串,该字符串包含格式正确的CREATE TABLE语句,该语句定义虚拟表中的列及其数据类型。与所有约束一样,该CREATE TABLE语句中的表名将被忽略。仅列名称和数据类型很重要。CREATE TABLE语句字符串不需要保存在持久性内存中。sqlite3_declare_vtab() 例程返回后,可以立即释放和/或重用该字符串。

xCreate方法不需要初始化sqlite3_vtab对象的pModule,nRef和zErrMsg字段。SQLite核心将负责这项工作。

该xCreate应该返回SQLITE_OK如果成功创建新的虚拟表,或SQLITE_ERROR如果它不成功。如果未成功,则不得分配sqlite3_vtab结构。如果失败,则可以选择在* pzErr中返回错误消息。必须使用SQLite内存分配函数(例如sqlite3_malloc()sqlite3_mprintf())分配用于保存错误消息字符串 的空间,因为在将错误报告给应用程序之后,SQLite核心将尝试使用sqlite3_free()释放空间。

如果省略xCreate方法(保留为NULL指针),则虚拟表是具有别名的虚拟表。无法使用CREATE VIRTUAL TABLE创建虚拟表的新实例,并且只能通过其模块名称使用虚拟表。请注意,3.9.0(2015-10-14)之前的SQLite版本不理解仅同名虚拟表,如果由于未检查xCreate方法而试图对仅同名虚拟表创建CREATE VIRTUAL TABLE,则会出现段错误为空。

如果xCreate方法是完全相同的指针作为xConnect方法,指示该虚拟表不需要初始化后备存储器。这样的虚拟表可以用作同义虚拟表 或使用CREATE VIRTUAL TABLE或同时用作命名虚拟表。

2.1.1。虚拟表中的隐藏列

如果列数据类型包含特殊关键字“ HIDDEN”(大小写字母的任意组合),则该关键字将从列数据类型名称中省略,并且该列在内部被标记为隐藏列。隐藏列与普通列在三个方面不同:

例如,如果将以下SQL传递给sqlite3_declare_vtab()

创建表x(a隐藏的VARCHAR(12),b INTEGER,c INTEGER隐藏);

然后,将使用两个隐藏列以及数据类型为“ VARCHAR(12)”和“ INTEGER”来创建虚拟表。

FTS3虚拟表实现中可以看到使用隐藏列的示例,其中每个FTS虚拟表都包含一个FTS隐藏列,用于将信息从虚拟表传递到FTS辅助功能以及FTS MATCH运算符。

2.1.2。表值函数

一个虚拟表,其中包含隐藏的列可用于像在从子句表值函数的SELECT语句。表值函数的参数成为对虚拟表的HIDDEN列的约束。

例如,“ generate_series”扩展名(位于源树ext / misc / series.c 文件中)实现具有以下模式的同名虚拟表

创建表generate_series(
  价值,
  开始隐藏,
  停止隐藏,
  隐藏的步骤
);

此表的实现中的sqlite3_module.xBestIndex方法检查针对HIDDEN列的相等约束,并将其用作输入参数来确定要生成的整数“值”输出的范围。合理的默认值用于任何不受约束的列。例如,列出5至50之间的所有整数:

从generate_series(5,50)中选择值

上一个查询等效于以下内容:

从generate_series的SELECT值开始= 5 AND停止= 50;

虚拟表名称上的参数按 顺序与隐藏列匹配。参数的数量可以小于隐藏列的数量,在这种情况下,后面的隐藏列不受限制。但是,如果参数多于虚拟表中的隐藏列,则会导致错误。

2.1.3。无行虚拟表

从SQLite版本3.14.0(2016-08-08)开始,传递到sqlite3_declare_vtab()的CREATE TABLE语句可能包含WITHOUT ROWID子句。对于无法轻松将虚拟表行映射为唯一整数的情况,这很有用。包含WITHOUT ROWID的CREATE TABLE语句必须将一个或多个列定义为PRIMARY KEY。PRIMARY KEY的每一列都必须分别为NOT NULL,并且每一行的所有列都必须共同唯一。

请注意,SQLite不会对WITHOUT ROWID虚拟表强制执行PRIMARY KEY。实施是基础虚拟表实现的责任。但是SQLite确实假定PRIMARY KEY约束是有效的-标识的列实际上是UNIQUE且不是NULL-并且它使用该假设来优化针对虚拟表的查询。

在WITHOUT ROWID虚拟表上,当然不能访问rowid列。

所述的XUpdate方法的最初目的是围绕具有 ROWID作为一个单一的值。在使用XUpdate方法已经扩大,以适应地方的ROWID的任意PRIMARY KEY,但PRIMARY KEY仍然必须只有一个列。因此,SQLite将拒绝任何具有多个PRIMARY KEY列和非NULL xUpdate方法的WITHOUT ROWID虚拟表。

2.2。xConnect方法

int(* xConnect)(sqlite3 *,无效* pAux,
             int argc,char * const * argv,
             sqlite3_vtab ** ppVTab,
             char ** pzErr);

xConnect方法与xCreate非常相似。它具有相同的参数,并像xCreate一样构造一个新的sqlite3_vtab结构。并且它还必须像xCreate一样调用sqlite3_declare_vtab()

区别在于,调用xConnect可以建立与现有虚拟表的新连接,而调用xCreate可以从头开始创建新的虚拟表。

仅当虚拟表具有某种必须在首次创建虚拟表时初始化的后备存储时,xCreate和xConnect方法才不同。xCreate方法创建并初始化后备存储。xConnect方法仅连接到现有的后备存储。当xCreate和xConnect相同时,该表是同义的虚拟表

例如,考虑一个虚拟表实现,该实现提供对磁盘上现有逗号分隔值(CSV)文件的只读访问。无需为此类虚拟表创建或初始化后备存储(因为磁盘上已经存在CSV文件),因此该模块的xCreate和xConnect方法将是相同的。

另一个示例是实现全文索引的虚拟表。xCreate方法必须创建并初始化数据结构以保存该索引的字典和发布列表。另一方面,xConnect方法仅需查找和使用现有字典并发布由先前xCreate调用创建的列表。

该xConnect方法必须返回SQLITE_OK如果成功创建新的虚拟表,或SQLITE_ERROR如果它不成功。如果未成功,则不得分配sqlite3_vtab结构。如果失败,则可以选择在* pzErr中返回错误消息。必须使用SQLite内存分配函数(例如sqlite3_malloc()sqlite3_mprintf())分配用于保存错误消息字符串 的空间,因为在将错误报告给应用程序之后,SQLite核心将尝试使用sqlite3_free()释放空间。

尽管虚拟表不需要初始化后备存储,但sqlite3_module对象的xCreate和xConnect指针可能指向同一函数,但每个虚拟表实现都需要使用xConnect方法。

2.3。xBestIndex方法

SQLite使用虚拟表模块的xBestIndex方法来确定访问虚拟表的最佳方法。xBestIndex方法具有这样的原型:

int(* xBestIndex)(sqlite3_vtab * pVTab,sqlite3_index_info *);

SQLite核心通过填充sqlite3_index_info结构的某些字段并将指向该结构的指针作为第二个参数传递到xBestIndex中,从而与xBestIndex方法进行通信。xBestIndex方法填充了构成回复的此结构的其他字段。该sqlite3_index_info结构如下:

struct sqlite3_index_info {
  / *输入* /
  const int nConstraint; / *约束中的条目数* /
  const struct sqlite3_index_constraint {
     int iColumn; / *列受约束。-1代表ROWID * /
     未签名的char op; / *约束运算符* /
     可用的未签名字符;/ *如果此约束可用,则为真* /
     int iTermOffset; / *内部使用-xBestIndex应该忽略* /
  } * const aConstraint; / * WHERE子句约束表* /
  const int nOrderBy; / * ORDER BY子句中的术语数* /
  const struct sqlite3_index_orderby {
     int iColumn; / *列号* /
     未签名的字符描述 / *对于DESC为True。ASC错误。* /
  } * const aOrderBy; / * ORDER BY子句* /

  / *输出* /
  struct sqlite3_index_constraint_usage {
    int argvIndex; / *如果> 0,则约束是xFilter的argv的一部分* /
    未签名的字符省略;/ *不要为此约束编写测试代码* /
  } * const aConstraintUsage;
  int idxNum; / *用于标识索引的数字* /
  char * idxStr; / *字符串,可能从sqlite3_malloc获得* /
  int needToFreeIdxStr; / *如果为true,则使用sqlite3_free()免费idxStr * /
  int orderByConsumed; / *如果已经订购了输出,则为True * /
  双倍估算费用;/ *使用此索引的估计成本* /
  / *以下字段仅在SQLite 3.8.2和更高版本中可用* /
  sqlite3_int64估计行; / *预计返回的行数* /
  / *以下字段仅在SQLite 3.9.0和更高版本中可用* /
  int idxFlags; / * SQLITE_INDEX_SCAN_ *标志的掩码* /
  / *以下字段仅在SQLite 3.10.0和更高版本中可用* /
  sqlite3_uint64 colUsed; / *输入:语句使用的列的掩码* /
};

请注意“ estimatedRows”,“ idxFlags”和colUsed字段上的警告。这些字段分别与SQLite版本3.8.2、3.9.0和3.10.0一起添加。任何读取或写入这些字段的扩展程序都必须首先检查所使用的SQLite库的版本是否大于或等于适当的版本-也许将sqlite3_libversion_number()返回的值 与常量3008002、3009000和/或3010000进行比较。尝试访问由较旧版本的SQLite创建的sqlite3_index_info结构中的这些字段的结果未定义。

此外,还有一些已定义的常量:

#定义SQLITE_INDEX_CONSTRAINT_EQ 2
#定义SQLITE_INDEX_CONSTRAINT_GT 4
#定义SQLITE_INDEX_CONSTRAINT_LE 8
#定义SQLITE_INDEX_CONSTRAINT_LT 16
#定义SQLITE_INDEX_CONSTRAINT_GE 32
#定义SQLITE_INDEX_CONSTRAINT_MATCH 64
#定义SQLITE_INDEX_CONSTRAINT_LIKE 65 / * 3.10.0及更高版本* /
#定义SQLITE_INDEX_CONSTRAINT_GLOB 66 / * 3.10.0及更高版本* /
#定义SQLITE_INDEX_CONSTRAINT_REGEXP 67 / * 3.10.0及更高版本* /
#define SQLITE_INDEX_CONSTRAINT_NE 68 / * 3.21.0及更高版本* /
#定义SQLITE_INDEX_CONSTRAINT_ISNOT 69 / * 3.21.0及更高版本* /
#定义SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70 / * 3.21.0及更高版本* /
#define SQLITE_INDEX_CONSTRAINT_ISNULL 71 / * 3.21.0及更高版本* /
#定义SQLITE_INDEX_CONSTRAINT_IS 72 / * 3.21.0及更高版本* /
#定义SQLITE_INDEX_CONSTRAINT_FUNCTION 150 / * 3.25.0及更高版本* /
#定义SQLITE_INDEX_SCAN_UNIQUE 1 / *最多扫描1行访问* /

SQLite核心在编译涉及虚拟表的查询时会调用xBestIndex方法。换句话说,SQLite在运行sqlite3_prepare()或等效版本时会调用此方法。通过调用此方法,SQLite核心对虚拟表说,它需要访问虚拟表中某些行的子集,并且它想知道执行该访问的最有效方法。xBestIndex方法回复信息,SQLite核心随后可以使用这些信息进行有效的虚拟表搜索。

在编译单个SQL查询时,SQLite核心可能使用sqlite3_index_info中的不同设置多次调用xBestIndex 。然后,SQLite核心将选择看起来能够提供最佳性能的组合。

在调用此方法之前,SQLite核心使用有关当前正在尝试处理的查询的信息来初始化sqlite3_index_info结构的实例。此信息主要源自查询的WHERE子句和ORDER BY或GROUP BY子句,如果查询是联接,则还源自任何ON或USING子句。SQLite核心提供给xBestIndex方法的信息保存在标记为“输入”的结构部分中。“输出”部分初始化为零。

sqlite3_index_info结构中的信息是短暂的,并且xBestIndex方法返回时可能会被覆盖或释放。如果xBestIndex方法需要记住sqlite3_index_info结构的任何部分 ,则应进行复制。必须注意将副本存储在将要释放的位置,例如,在NeedToFreeIdxStr设置为1的idxStr字段中。

请注意,xBestIndex将始终在xFilter之前调用,因为xBestIndex的idxNum和idxStr输出是xFilter的必需输入。但是,不能保证在成功执行xBestIndex之后将调用xFilter。

每个虚拟表实现都需要xBestIndex方法。

2.3.1。输入项

SQLite核心尝试与虚拟表进行通信的主要内容是可用来限制需要搜索的行数的约束。aConstraint []数组为每个约束包含一个条目。该数组中将恰好有nConstraint条目。

每个约束通常对应于WHERE子句中或USING或ON子句中形式为以下形式的术语

列OP EXPR

其中“ column”是虚拟表中的列,OP是诸如“ =”或“ <”之类的运算符,而EXPR是任意表达式。因此,例如,如果WHERE子句包含这样的术语:

a = 5

然后,约束条件之一将在“ a”列上,并带有运算符“ =”和表达式“ 5”。约束不必具有WHERE子句的文字表示。查询优化器可能会对WHERE子句进行转换,以便提取尽可能多的约束。因此,例如,如果WHERE子句包含如下内容:

x在10和100与999之间> y

查询优化器可能会将其转换为三个单独的约束:

x> = 10
x <= 100
y <999

对于每个此类约束,aConstraint []。iColumn字段指示哪一列出现在约束的左侧。虚拟表的第一列是列0。虚拟表的rowid是列-1。aConstraint []。op字段指示使用哪个运算符。SQLITE_INDEX_CONSTRAINT_ *常数将整数常数映射为运算符值。列按xCreatexConnect方法中对sqlite3_declare_vtab()的调用定义的顺序出现 。确定列索引时,将对隐藏的列进行计数。

如果为虚拟表定义了xFindFunction()方法,并且如果xFindFunction()有时返回SQLITE_INDEX_CONSTRAINT_FUNCTION或更大,则约束也可能采用以下形式:

FUNCTION(栏,EXPR)

在这种情况下,aConstraint []。op值与xFindFunction()为FUNCTION返回的值相同。

aConstraint []数组包含有关适用于虚拟表的所有约束的信息。但是,由于联接中表的排序方式,某些约束可能无法使用。因此,xBestIndex方法必须仅考虑具有aConstraint []。usable标志为true的约束。

除了WHERE子句约束之外,SQLite内核还向xBestIndex方法告知有关ORDER BY子句的信息。(在聚合查询中,SQLite核心可能会将GROUP BY子句信息放在ORDER BY子句信息的位置,但这一事实与xBestIndex方法没有任何区别。)如果ORDER BY子句的所有术语都是“虚拟表,则nOrderBy将是ORDER BY子句中的术语数,而aOrderBy []数组将标识order by子句中每个术语的列,以及该列是ASC还是DESC。

在SQLite版本3.10.0(2016-01-06)和更高版本中,colUsed字段可用于指示所准备的语句实际使用虚拟表的哪些字段。如果设置了colUsed的最低位,则表示使用了第一列。第二最低位对应于第二列。依此类推。如果设置了colUsed的最高有效位,则意味着将使用除前63列以外的一个或多个列。如果xFilter方法需要列使用情况信息 ,则必须将所需的位编码为idxNum或idxStr输出字段。

2.3.1.1。LIKE,GLOB,REGEXP和MATCH函数

对于LIKE,GLOB,REGEXP和MATCH运算符,aConstraint []。iColumn值是虚拟表列,它是运算符的左操作数。但是,如果将这些运算符表示为函数调用而不是运算符,则aConstraint []。iColumn值将引用虚拟表列,该列是该函数的第二个参数:

LIKE(EXPRcolumn
GLOB(EXPRcolumn
REGEXP(EXPRcolumn
MATCH(EXPRcolumn

因此,就xBestIndex()方法而言,以下两种形式是等效的:

LIKE EXPR
LIKE(EXPRcolumn

查看函数的第二个参数的这种特殊行为仅在LIKE,GLOB,REGEXP和MATCH函数中发生。对于所有其他函数,aConstraint []。iColumn值引用该函数的第一个参数。

但是,LIKE,GLOB,REGEXP和MATCH的此特殊功能不适用于xFindFunction()方法。所述 xFindFunction()方法总是键关闭的LIKE,GLOB,REGEXP或MATCH操作者的左操作数的不过关的第一个参数的那些运营商的函数调用等同物。

2.3.2。产出

鉴于以上所有信息,xBestIndex方法的工作是找出搜索虚拟表的最佳方法。

xBestIndex方法使用将索引策略传达给xFilter 方法的信息填充idxNum和idxStr字段。就SQLite核心而言,idxNum和idxStr中的信息是任意的。SQLite核心仅将信息复制到xFilter方法。只要xBestIndex和xFilter同意该含义是什么,任何期望的含义都可以分配给idxNum和idxStr。

idxStr值可以是从SQLite内存分配函数(例如sqlite3_mprintf())获得的字符串。如果是这种情况,则必须将needToFreeIdxStr标志设置为true,以便SQLite内核在完成该字符串后将知道在该字符串上调用sqlite3_free(),从而避免了内存泄漏。idxStr值也可以是静态常量字符串,在这种情况下,needToFreeIdxStr布尔值应保持为false。

如果虚拟表将按ORDER BY子句指定的顺序输出行,则可以将orderByConsumed标志设置为true。如果输出不是自动按照正确的顺序排列,则必须将orderByConsumed保留为其默认的false设置。这将向SQLite核心表明,从虚拟表中出来的数据将需要对数据进行单独的排序。

应该将estimatedCost字段设置为对虚拟表执行此查询所需的磁盘访问操作的估计数量。SQLite核心通常会使用不同的约束多次调用xBestIndex,获取多个成本估算,然后选择给出最低估算的查询计划。SQLite核心在调用xBestIndex之前将estimateCost初始化为一个非常大的值,因此,如果xBestIndex确定当前的参数组合是不希望的,则可以使estimatedCost字段保持不变以阻止其使用。

如果SQLite的当前版本为3.8.2或更高版本,则估计行字段可以设置为对所提出的查询计划返回的行数的估计。如果未显式设置此值,则使用25行的默认估计。

如果SQLite的当前版本为3.9.0或更高版本,则idxFlags字段可以设置为SQLITE_INDEX_SCAN_UNIQUE以指示在给定输入约束的情况下,虚拟表将仅返回零或一行。在更高版本的SQLite中可能会理解idxFlags字段的其他位。

对于sqlite3_index_info结构的输入部分中的每个nConstraint约束,aConstraintUsage []数组均包含一个元素 。xBestIndex使用aConstraintUsage []数组来告诉核心如何使用约束。

xBestIndex方法可以将aConstraintUsage []。argvIndex条目设置为大于零的值。确切地,一个条目应该设置为1,另一个设置为2,另一个设置为3,依此类推,最多xbestIndex方法所需。然后,将相应约束的EXPR作为argv []参数传递到xFilter。

例如,如果aConstraint [3] .argvIndex设置为1,则在调用xFilter时,传递给xFilter的argv [0]将具有aConstraint [3]约束的EXPR值。

默认情况下,SQLite核心会对接收到的虚拟表的每一行进行双重检查。如果这种检查是多余的,则xBestFilter方法可以通过设置aConstraintUsage []。omit来抑制该双重检查。

2.3.3。返回值

如果成功,xBestIndex方法应返回SQLITE_OK。如果发生任何类型的致命错误,则应返回适当的错误代码(例如:SQLITE_NOMEM)。

如果xBestIndex返回SQLITE_CONSTRAINT,则不表示错误。而是,SQLITE_CONSTRAINT指示在查询计划中不应使用指定的输入参数的特定组合。SQLITE_CONSTRAINT返回对于具有必需参数的表值函数很有用。如果aConstraint []。usable字段的必需参数之一为false,则xBestIndex方法应返回SQLITE_CONSTRAINT。

以下示例将更好地说明如何使用SQLITE_CONSTRAINT作为xBestIndex的返回值:

SELECT * FROM realtab,tablevaluedfunc(realtab.x);

假设“ tablevaluedfunc”的第一个隐藏列为“ param1”,则上面的查询在语义上等效于此:

SELECT * FROM realtab,tablevaluedfunc
 WHERE tablevaluedfunc.param1 = realtab.x;

查询计划者必须在此查询的许多可能实现之间做出决定,但要特别注意两个计划:

  1. 扫描realtab的所有行,并为每一行查找tablevaluedfunc中的行,其中param1等于realtab.x

  2. 扫描表值函数的所有行,并为每行在realtab中找到x等于tablevaluedfunc.param1的行。

xBestIndex方法将为上述每个潜在计划调用一次。对于计划1,param1列上SQLITE_CONSTRAINT_EQ约束的aConstraint []。usable标志将为true,因为“ param1 =?”的右侧值 约束是已知的,因为它是由外部realtab循环确定的。但是对于计划2,“ param1 =?”的aConstraint []。usable标志 将为假,因为右侧值由内部循环确定,因此是未知量。由于param1是表值函数的必需输入,因此xBestIndex方法在与计划2一起显示时应返回SQLITE_CONSTRAINT,指示缺少必需的输入。这将强制查询计划者选择计划1。

2.4。xDisconnect方法

int(* xDisconnect)(sqlite3_vtab * pVTab);

此方法释放到虚拟表的连接。只有sqlite3_vtab对象被销毁。虚拟表不会被破坏,并且与该虚拟表关联的任何后备存储都将保留。此方法取消xConnect的工作。

此方法是与虚拟表的连接的析构函数。将此方法与xDestroy进行对比。xDestroy是整个虚拟表的析构函数。

尽管对于特定的虚拟表有意义,但xDisconnect和xDestroy方法可以使用相同的功能,但每个虚拟表实现都需要使用xDisconnect方法。

2.5。xDestroy方法

int(* xDestroy)(sqlite3_vtab * pVTab);

就像xDisconnect方法一样,此方法释放与虚拟表的连接,并且还会破坏基础表的实现。此方法撤消xCreate的工作。

所述xDisconnect每当使用虚拟表的数据库连接关闭方法被调用。仅当针对虚拟表执行DROP TABLE语句时,才调用xDestroy方法。

尽管对于特定的虚拟表有意义,但xDisconnect和xDestroy方法可以使用相同的功能,但每个虚拟表实现都需要使用xDestroy方法。

2.6。xOpen方法

int(* xOpen)(sqlite3_vtab * pVTab,sqlite3_vtab_cursor ** ppCursor);

xOpen方法创建一个新的游标,该游标用于访问(读取和/或写入)虚拟表。成功调用此方法将为sqlite3_vtab_cursor(或子类)分配内存,初始化新对象,并使* ppCursor指向新对象。然后,成功的调用将返回SQLITE_OK

对于此方法的每次成功调用,SQLite核心稍后都会调用xClose方法来销毁分配的游标。

xOpen方法无需初始化sqlite3_vtab_cursor结构的pVtab字段 。SQLite核心将自动处理该琐事。

虚拟表实现必须能够支持任意数量的同时打开的游标。

最初打开时,光标处于未定义状态。在尝试定位或读取游标之前,SQLite核心将在游标上调用xFilter方法。

每个虚拟表实现都需要xOpen方法。

2.7。xClose方法

int(* xClose)(sqlite3_vtab_cursor *);

xClose方法关闭以前由xOpen打开的 游标。对于使用xOpen打开的每个游标,SQLite核心将始终调用一次xClose。

此方法必须释放由相应的xOpen调用分配的所有资源。该例程即使返回错误也不会再次被调用。关闭后,SQLite核心将不再使用 sqlite3_vtab_cursor

每个虚拟表实现都需要xClose方法。

2.8。xEof方法

int(* xEof)(sqlite3_vtab_cursor *);

如果指定的游标当前指向有效的数据行,则xEof方法必须返回false(零),否则返回true(非零)。每次xFilterxNext调用之后,SQL引擎都会立即调用此方法 。

每个虚拟表实现都需要xEof方法。

2.9。xFilter方法

int(* xFilter)(sqlite3_vtab_cursor *,int idxNum,const char * idxStr,
              int argc,sqlite3_value ** argv);

此方法开始搜索虚拟表。第一个参数是xOpen打开的游标。接下来的两个参数定义以前由xBestIndex选择的特定搜索索引。只要xFilter和xBestIndex同意什么含义,idxNum和idxStr的具体含义就无关紧要。

xBestIndex函数可能已使用sqlite3_index_info结构的aConstraintUsage []。argvIndex值请求了某些表达式的值。这些值使用argc和argv参数传递到xFilter。

如果虚拟表包含一个或多个符合搜索条件的行,则光标必须位于第一行的左侧。后续对xEof的调用必须返回false(零)。如果没有匹配的行,则游标必须处于会使xEof返回true(非零)的状态。SQLite引擎将使用xColumnxRowid方法访问该行内容。该xNext方法将被用于推进到下一行。

如果成功,此方法必须返回SQLITE_OK;如果发生错误,则必须返回sqlite 错误代码

每个虚拟表实现都需要xFilter方法。

2.10。xNext方法

int(* xNext)(sqlite3_vtab_cursor *);

xNext方法将虚拟表游标前进 到xFilter发起的结果集的下一行。如果在调用此例程时游标已经指向最后一行,则游标不再指向有效数据,并且随后对xEof方法的调用必须返回true(非零)。如果光标成功前进到另一行内容,则随后对xEof的调用必须返回false(零)。

如果成功,此方法必须返回SQLITE_OK;如果发生错误,则必须返回sqlite 错误代码

每个虚拟表实现都需要xNext方法。

2.11。xColumn方法

int(* xColumn)(sqlite3_vtab_cursor *,sqlite3_context *,int N);

SQLite核心调用此方法以便找到当前行的第N列的值。N是从零开始的,因此第一列的编号为0。xColumn方法可以使用以下接口之一将其结果返回给SQLite:

如果xColumn方法实现不调用上述任何函数,则该列的值默认为SQL NULL。

要引发错误,xColumn方法应使用result_text()方法之一来设置错误消息文本,然后返回适当的 错误代码。如果成功,xColumn方法必须返回SQLITE_OK

每个虚拟表实现都需要xColumn方法。

2.12。xRowid方法

int(* xRowid)(sqlite3_vtab_cursor * pCur,sqlite_int64 * pRowid);

成功调用此方法将导致* pRowid填充虚拟表光标pCur当前指向的行 的rowid。成功时此方法返回SQLITE_OK。失败时将返回适当的错误代码

每个虚拟表实现都需要xRowid方法。

2.13。xUpdate方法

int(* xUpdate)(
  sqlite3_vtab * pVTab,
  int argc,
  sqlite3_value ** argv,
  sqlite_int64 * pRowid
);

使用xUpdate方法对虚拟表进行所有更改。此方法可用于插入,删除或更新。

argc参数指定argv数组中的条目数。对于纯删除操作,argc的值为1;对于插入,替换或更新,argc的值为N + 2,其中N是表中的列数。在前一句中,N包括任何隐藏的列。

每个argv条目在C语言中都有一个非NULL值,但可能包含SQL值NULL。换句话说,它始终是真的 的argv [I]!= 0介于0和的argc-1 。但是,sqlite3_value_type(argv [i])== SQLITE_NULL可能是这种情况 。

argv [0]参数是虚拟表中要删除的行的rowid。如果argv [0]是SQL NULL,则不会发生删除。

argv [1]参数是要插入到虚拟表中的新行的rowid。如果argv [1]是SQL NULL,则实现必须为新插入的行选择一个rowid。随后的argv []条目包含虚拟表的列的值,并以声明这些列的顺序。列数将与使用sqlite3_declare_vtab()调用的xConnectxCreate方法做出的表声明匹配。包括所有隐藏的列。

在不使用rowid的情况下进行插入(argc> 1,argv [1]为SQL NULL)时,在使用ROWID的虚拟表上(但不在WITHOUT ROWID虚拟表上),实现必须将* pRowid设置为新插入的行;这将成为sqlite3_last_insert_rowid() 函数返回的值。在所有其他情况下,将此值设置为无害操作即可;如果argc == 1或argv [1]不是SQL NULL,则SQLite引擎将忽略* pRowid返回值。

每次对xUpdate的调用将属于以下情况之一。并不是说对argv [i]的引用意味着argv [i]对象中保存的SQL值,而不是argv [i]对象本身。

argc = 1
argv [0]≠NULL

DELETE:删除具有rowid或PRIMARY KEY等于argv [0]的单行。没有插入发生。

argc> 1
argv [0] = NULL

INSERT:插入新行,其中列值取自argv [2]及其后。在rowid虚拟表中,如果argv [1]是SQL NULL,则将自动生成一个新的唯一rowid。对于WITHOUT ROWID虚拟表,argv [1]将为NULL ,在这种情况下,实现应从argv [2]及后续版本中的相应列中获取PRIMARY KEY值。

argc> 1
argv [0]≠NULL
argv [0] = argv [1]

更新:具有rowid或PRIMARY KEY argv [0]的行将使用argv [2]和以下参数中的新值进行更新。

argc> 1
argv [0]≠NULL
argv [0]≠argv [1]

使用rowid或PRIMARY KEY更改进行更新:具有rowid或PRIMARY KEY argv [0]的行将使用argv [1]中的rowid或PRIMARY KEY以及argv [2]中的新值和以下参数进行更新。当一条SQL语句更新一个rowid时,就会发生这种情况,如下所示:

更新表SET rowid = rowid + 1 WHERE ...;

当且仅当xUpdate方法成功时,它才必须返回SQLITE_OK。如果发生故障,则xUpdate必须返回适当的 错误代码。如果发生故障,可以使用sqlite3_mprintf()sqlite3_malloc()之类的函数将pVTab-> zErrMsg元素替换为从SQLite分配的内存中存储的错误消息文本。

如果xUpdate方法违反了虚拟表的某些约束(包括但不限于尝试存储错误数据类型的值,尝试存储太大或太小的值或尝试更改只读值值),则xUpdate必须失败,并带有相应的错误代码

如果xUpdate方法正在执行UPDATE,则 可以使用sqlite3_value_nochange(X)来发现UPDATE语句实际修改了虚拟表的哪些列。该sqlite3_value_nochange(X)接口返回true为不更改列。在每个UPDATE上,SQLite将首先为表中的每个不变列分别调用 xColumn以获得该列的值。该xColumn方法可以检查,看看是否列不变在SQL级别通过调用sqlite3_vtab_nochange() 。如果xColumn看到未修改该列,则应使用sqlite3_result_xxxxx()之一返回而不设置结果 接口。只有在这种情况下,在xUpdate方法中sqlite3_value_nochange()才为true。如果xColumn确实调用了一个或多个sqlite3_result_xxxxx() 接口,则SQLite理解为该列的值发生了变化,并且xUpdate中对该列的sqlite3_value_nochange()调用将返回false。

在调用xUpdate方法时,可能在虚拟表实例上甚至在虚拟表的行上打开并使用了一个或多个sqlite3_vtab_cursor对象。xUpdate的实现必须准备好尝试从其他现有游标中删除或修改表的行。如果虚拟表不能容纳此类更改,则xUpdate方法必须返回错误代码

xUpdate方法是可选的。如果sqlite3_module中用于虚拟表的xUpdate指针是NULL指针,则该虚拟表是只读的。

2.14。xFindFunction方法

int(* xFindFunction)(
  sqlite3_vtab * pVtab,
  int nArg,
  const char * zName,
  void(** pxFunc)(sqlite3_context *,int,sqlite3_value **),
  无效** ppArg
);

sqlite3_prepare()期间调用此方法,以使虚拟表实现有机会重载函数。在不发生任何重载的情况下,可以将此方法设置为NULL。

当函数使用虚拟表中的列作为其第一个参数时,将调用此方法以查看虚拟表是否要重载该函数。前三个参数是输入:虚拟表,函数的参数数量和函数的名称。如果不需要重载,则此方法返回0。要重载该函数,此方法将新函数实现写入* pxFunc并将用户数据写入* ppArg,并返回1或SQLITE_INDEX_CONSTRAINT_FUNCTION与255之间的数字 。

从历史上看,xFindFunction()的返回值是零或一。零表示该函数未重载,而一则表示该函数已重载。在版本3.25.0(2018-09-15)中添加了返回SQLITE_INDEX_CONSTRAINT_FUNCTION值或更高值的 功能。如果xFindFunction返回 SQLITE_INDEX_CONSTRAINT_FUNCTION或更大,则表示该函数接受两个参数,并且该函数可以在查询的WHERE子句中用作布尔值,并且虚拟表可以利用该函数来加快查询结果。当xFindFunction返回SQLITE_INDEX_CONSTRAINT_FUNCTION或更大值时,返回的值将成为传入的约束之一的sqlite3_index_info.aConstraint.op值。xBestIndex()和第二个参数成为与传递给xFilter()的约束相对应的值。这使xBestIndex()/ xFilter实现可以使用该函数加快其搜索速度。

最初在Geopoly模块的实现中使用了具有xFindFunction()返回值 SQLITE_INDEX_CONSTRAINT_FUNCTION技术。该模块返回SQLITE_INDEX_CONSTRAINT_FUNCTION为的xFindFunction()方法geopoly_overlap() SQL函数,并将其返回SQLITE_INDEX_CONSTRAINT_FUNCTION + 1为geopoly_within() SQL函数。这允许对查询进行搜索优化,例如:

SELECT * FROM geopolytab在哪里geopoly_overlap(_shape,$ query_polygon);

请注意,中缀函数(LIKEGLOBREGEXPMATCH)反转其参数的顺序。因此,“ like(A,B)”通常与“ B like A”相同。但是,xFindFunction()始终看起来是最左边的参数,而不是第一个逻辑参数。因此,对于形式为“像A一样的B”,SQLite将查看左侧的操作数“ B”,如果该操作数是虚拟表列,它将在该虚拟表上调用xFindFunction()方法。但是,如果改为使用形式“ like(A,B)”,则SQLite会检查A项以查看其是否为虚拟表的列,如果是,则为A列的虚拟表调用xFindFunction()方法。

该例程返回的函数指针必须在第一个参数中给出的sqlite3_vtab对象的生存期内有效。

2.15。xBegin方法

int(* xBegin)(sqlite3_vtab * pVTab);

此方法在虚拟表上开始事务。这是可选方法。sqlite3_module的xBegin指针 可以为NULL。

始终在此方法之后是对xCommitxRollback方法的一次调用 。虚拟表事务不会嵌套,因此在没有对xCommitxRollback进行中间调用的情况下,不会在单个虚拟表上多次调用xBegin方法。在xBegin与对应的xCommitxRollback之间可能会发生对其他方法的多次调用。

2.16。xSync方法

int(* xSync)(sqlite3_vtab * pVTab);

此方法表示在虚拟表上开始两阶段提交。这是可选方法。sqlite3_module的xSync指针 可以为NULL。

仅在调用xBegin方法之后,在xCommitxRollback之前调用此方法。为了实现两阶段提交,在调用任何虚拟表上的xCommit方法之前,将在所有虚拟表上调用xSync方法。如果任何xSync方法失败,则将回滚整个事务。

2.17。xCommit方法

int(* xCommit)(sqlite3_vtab * pVTab);

此方法导致虚拟表事务提交。这是可选方法。sqlite3_module的xCommit指针 可以为NULL。

始终在先前调用xBeginxSync之后调用此方法。

2.18。xRollback方法

int(* xRollback)(sqlite3_vtab * pVTab);

此方法导致虚拟表事务回滚。这是可选方法。sqlite3_module的xRollback指针 可以为NULL。

始终在对xBegin的先前调用之后始终调用此方法。

2.19。xRename方法

int(* xRename)(sqlite3_vtab * pVtab,const char * zNew);

此方法提供通知,通知虚拟表实现将为虚拟表赋予新名称。如果此方法返回SQLITE_OK,则SQLite重命名该表。如果此方法返回错误代码,则将阻止重命名。

xRename方法是可选的。如果省略,则不能使用ALTER TABLE RENAME命令重命名虚拟表。

所述PRAGMA legacy_alter_table设置之前调用此方法使能,以及用于legacy_alter_table值被此方法结束后恢复。这对于使用影子表的虚拟表的正确操作是必需的,在虚拟表中,影子表必须重命名以匹配新的虚拟表名称。如果legacy_alter_format关闭,则每次xRename方法尝试更改影子表的名称时,都会为虚拟表调用xConnect方法。

2.20。xSavepoint,xRelease和xRollbackTo方法

int(* xSavepoint)(sqlite3_vtab * pVtab,int);
int(* xRelease)(sqlite3_vtab * pVtab,int);
int(* xRollbackTo)(sqlite3_vtab * pVtab,int);

这些方法为虚拟表实现提供了实现嵌套事务的机会。它们始终是可选的,并且只能在SQLite 3.7.7(2011-06-23)及更高版本中调用。

调用xSavepoint(X,N)时,这是向虚拟表X发出的信号,它应将其当前状态保存为保存点N。后续对xRollbackTo(X,R)的调用意味着虚拟表的状态应返回到最后一次调用xSavepoint(X,R)时的状态。调用xRollbackTo(X,R)将使所有保存点的N> R无效;任何无效的保存点都不会回滚或释放,而无需先通过调用xSavepoint()进行重新初始化。调用xRelease(X,M)会使N> = M的所有保存点无效。

xSavepoint(),xRelease()或xRollbackTo()方法都不会被调用,除非在两次调用xBegin()以及xCommit()或xRollback()之间。

2.21。xShadowName方法

一些虚拟表实现(例如FTS3FTS5RTREE)利用真实(非虚拟)数据库表来存储内容。例如,将内容插入FTS3虚拟表时,数据最终存储在名为“%_content”,“%_ segdir”,“%_ segments”,“%_ stat”和“%_docsize”的真实表中,其中“%” ”是原始虚拟表的名称。存储虚拟表内容的辅助实际表称为“影子表”。有关更多信息,请参见(1),(2)和(3)。

存在xShadowName方法,以允许SQLite确定某个实际表是否实际上是虚拟表的影子表。

如果满足以下所有条件,则SQLite会将真实表理解为影子表:

如果SQLite将表识别为影子表,并且设置了 SQLITE_DBCONFIG_DEFENSIVE标志,则影子表对于普通SQL语句是只读的。影子表仍然可以写入,但是只能由从某些虚拟表实现的方法之一中调用的SQL编写。

xShadowName方法的重点是保护影子表的内容不受恶意SQL的破坏。每个使用影子表的虚拟表实现都应该能够检测并处理损坏的影子表内容。但是,特定虚拟表实现中的错误可能允许故意损坏的影子表导致崩溃或其他故障。xShadowName机制通过防止普通SQL语句故意破坏影子表来避免零时差攻击。

默认情况下,影子表是读/写的。仅当 使用sqlite3_db_config()设置SQLITE_DBCONFIG_DEFENSIVE标志时,影子表才变为只读。默认情况下,影子表需要读取/写入,以保持向后兼容性。例如,由CLI.dump命令生成的SQL文本 直接写入影子表。