SQLite 4.9的虚拟表机制(十四)

返回:SQLite—系列文章目录   

上一篇:SQLite 4.9的 OS 接口或“VFS”(十三)

下一篇:SQLite—系列文章目录   

1. 引言

虚拟表是向打开的 SQLite 数据库连接注册的对象。从SQL语句的角度来看, 虚拟表对象与任何其他表或视图类似。 但在幕后,虚拟表上的查询和更新 调用虚拟表对象的回调方法,而不是 读取和写入数据库文件。

虚拟表机制允许应用程序发布 可从 SQL 语句访问的接口,就像它们是 表。SQL 语句几乎可以对 他们可以对真实表执行的虚拟表,具有以下功能 异常:

  • 不能在虚拟表上创建触发器。
  • 不能在虚拟表上创建其他索引。 (虚拟表可以有索引,但必须内置到 虚拟表实现。无法添加索引 单独使用 CREATE INDEX 语句。
  • 无法运行 ALTER TABLE ...针对虚拟表的 ADD COLUMN 命令。

单个虚拟表实现可能会施加额外的 约束。例如,某些虚拟实现可能提供 只读表。或者,某些虚拟表实现可能允许 INSERT 或 DELETE,但不允许 UPDATE。或者一些虚拟表实现 可能会限制可以进行的更新类型。

虚拟表可能表示内存中数据结构。 或者,它可能表示磁盘上不在 SQLite 格式。或者,应用程序可能会计算 按需虚拟表。

以下是虚拟表的一些现有和假定用途:

  • 全文搜索界面
  • 使用 R 树的空间索引
  • 自检 SQLite 数据库文件的磁盘内容 (dbstat 虚拟表)
  • 读取和/或写入逗号分隔值 (CSV) 的内容 文件
  • 像访问数据库表一样访问主机的文件系统
  • 在统计信息包(如 R)中启用数据的 SQL 操作

请参阅虚拟表列表页面,查看实际的更长列表 虚拟表实现。

1.1. 用法

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

创建虚拟表-stmt: 隐藏

创造虚拟桌子如果不存在架构名称.表名用模块名称(module-参数),

CREATE VIRTUAL TABLE 语句创建一个新表 调用 table-name,派生自类 module-namemodule-name 是为 virtual table 注册的名称 sqlite3_create_module() 接口。

CREATE VIRTUAL TABLE tablename USING modulename;

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

CREATE VIRTUAL TABLE tablename USING modulename(arg1, arg2, ...);

模块参数的格式非常通用。每个模块参数可以包含关键字、字符串文本、标识符、数字和 标点。每个模块参数都传递为 写入(作为文本)到虚拟表实现的构造函数方法中 当虚拟 table 已创建,该构造函数负责解析和 解释论点。参数语法足够通用 如果需要,虚拟表实现可以解释其 参数作为普通 CREATE TABLE 语句中的列定义。 实施还可以对 参数。

创建虚拟表后,可以像使用任何其他表一样使用它 表,具有上述例外情况,并由特定虚拟 表实现。使用普通的 DROP TABLE 语法销毁虚拟表。

1.1.1. 临时虚拟表

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

CREATE VIRTUAL TABLE temp.tablename USING module(arg1, ...);

1.1.2. 同名虚拟表

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

同名虚拟表的一个示例是 dbstat 虚拟表。 要将 dbstat 虚拟表用作同名虚拟表, 只需针对“dbstat”进行查询 模块名称,就好像它是一个普通的表一样。(请注意,SQLite 必须使用SQLITE_ENABLE_DBSTAT_VTAB选项进行编译,以包括 生成中的 dbstat 虚拟表。

SELECT * FROM dbstat;

如果虚拟表的 xCreate 方法完全相同,则该虚拟表是同名的 函数作为 xConnect 方法,或者如果 xCreate 方法为 NULL。 首次创建虚拟表时调用 xCreate 方法 使用 CREATE VIRTUAL TABLE 语句。xConnect 方法 每当调用时 数据库连接附加到架构或重新分析架构。当这两种方法 相同,表示虚拟表没有持久性 需要创建和销毁的状态。

1.1.3. 仅限同名的虚拟表

如果 xCreate 方法为 NULL,则禁止对该虚拟表使用 CREATE VIRTUAL TABLE 语句。 虚拟表是“仅限同名的虚拟表”。 仅限同名的虚拟表可用作表值函数。

请注意,在版本 3.9.0 (2015-10-14) 之前, SQLite 未检查 xCreate 方法 for 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结构用于将信息传递到 并从实现 虚拟表。

在运行 CREATE VIRTUAL TABLE 语句之前,模块 该语句中指定的必须注册到数据库 连接。这是使用 sqlite3_create_module() 或 sqlite3_create_module_v2() 接口之一完成的:

int sqlite3_create_module(
  sqlite3 *db,               /* SQLite connection to register module with */
  const char *zName,         /* Name of the module */
  const sqlite3_module *,    /* Methods for the module */
  void *                     /* Client data for xCreate/xConnect */
);
int sqlite3_create_module_v2(
  sqlite3 *db,               /* SQLite connection to register module with */
  const char *zName,         /* Name of the module */
  const sqlite3_module *,    /* Methods for the module */
  void *,                    /* Client data for xCreate/xConnect */
  void(*xDestroy)(void*)     /* Client data destructor function */
);

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

struct sqlite3_module {
  int iVersion;
  int (*xCreate)(sqlite3*, void *pAux,
               int argc, char *const*argv,
               sqlite3_vtab **ppVTab,
               char **pzErr);
  int (*xConnect)(sqlite3*, void *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 (*xRename)(sqlite3_vtab *pVtab, const char *zNew);
  /* The methods above are in version 1 of the sqlite_module object. Those 
  ** below are for version 2 and greater. */
  int (*xSavepoint)(sqlite3_vtab *pVTab, int);
  int (*xRelease)(sqlite3_vtab *pVTab, int);
  int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
  /* The methods above are in versions 1 and 2 of the sqlite_module object.
  ** Those below are for version 3 and greater. */
  int (*xShadowName)(const char*);
  /* The methods above are in versions 1 through 3 of the sqlite_module object.
  ** Those below are for version 4 and greater. */
  int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
                    const char *zTabName, int mFlags, char **pzErr);
};

模块结构定义了每个虚拟的所有方法 table 对象。模块结构还包含 iVersion 字段,该字段 定义模块表结构的特定版本。现在 iVersion 始终为 4 或更少,但在 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 源代码树包含许多适合复制的虚拟表实现, 包括:

  • templatevtab.c → 专门创建的虚拟表,用作模板 其他自定义虚拟表。
  • series.c → generate_series()表值函数的实现。
  • json.c → 包含 json_each() 和 json_tree() 表值的源 功能。
  • csv.c → 读取 CSV 文件的虚拟表。

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

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

2. 虚拟表方法

2.1. xCreate 方法

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

调用 xCreate 方法以创建虚拟表的新实例 响应 CREATE VIRTUAL TABLE 语句。 如果 xCreate 方法与 xConnect 方法的指针相同,则 虚拟表是同名的虚拟表。 如果省略 xCreate 方法(如果它是 NULL 指针),则 virtual table 是一个仅限同名的虚拟表。

db 参数是指向 SQLite 数据库连接的指针,该连接 正在执行 CREATE VIRTUAL TABLE 语句。 pAux 参数是客户端数据指针的副本,该指针是 注册虚拟表模块的 sqlite3_create_module() 或 sqlite3_create_module_v2() 调用的第四个参数。 argv 参数是指向以 null 结尾的字符串的 argc 指针数组。 第一个字符串 argv[0] 是正在调用的模块的名称。这 module name 是作为 sqlite3_create_module() 的第二个参数和正在运行的 CREATE VIRTUAL TABLE 语句的 USING 子句的参数提供的名称。 第二个 argv[1],是新虚拟 正在创建表。主数据库的数据库名称为“main”,或者 “temp”表示 TEMP 数据库,或在附加数据库的 ATTACH 语句末尾给出的名称。数组的第三个元素 argv[2], 是新虚拟表的名称,在 TABLE 之后指定 CREATE VIRTUAL 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() 例程返回,就会立即释放和/或重用。

xConnect 方法还可以选择请求特殊功能 通过对虚拟表进行一次或多次调用来获取 sqlite3_vtab_config() 接口:

int sqlite3_vtab_config(sqlite3 *db, int op, ...);

对 sqlite3_vtab_config() 的调用是可选的。但为了最大 安全性,建议虚拟表实现 调用“sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY)”,如果 不会从触发器或视图内部使用虚拟表。

xCreate 方法不需要初始化 pModule、nRef 和 zErrMsg sqlite3_vtab对象的字段。SQLite核心将负责 那件苦差事。

如果 xCreate 成功,则 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 方法是否为 null。

如果 xCreate 方法与 xConnect 方法的指针完全相同, 这表示虚拟表不需要初始化后备 商店。这样的虚拟表可以用作同名虚拟表,也可以使用 CREATE VIRTUAL TABLE 或两者用作命名虚拟表。

2.1.1. 虚拟表中的隐藏列

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

  • 隐藏列未在 返回的数据集中列出 “PRAGMA table_info”,
  • 隐藏列不包括在“*”的扩展中 表达式,以及
  • 隐藏列不包括在隐式列列表中 由缺少显式列列表的 INSERT 语句使用。

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

CREATE TABLE x(a HIDDEN VARCHAR(12), b INTEGER, c INTEGER Hidden);

然后,将创建具有两个隐藏列的虚拟表, 数据类型为 “VARCHAR(12)” 和 “INTEGER”。

在 FTS3 虚拟中可以看到隐藏列的示例使用 表实现,其中每个 FTS 虚拟表 包含一个 FTS 隐藏列,用于从 虚拟表转换为 FTS 辅助函数和 FTS MATCH 运算符。

2.1.2. 表值函数

包含隐藏列的虚拟表可以像 SELECT 语句的 FROM 子句中的表值函数。 表值函数的参数成为 虚拟表的 HIDDEN 列。

例如,“generate_series”扩展名(位于源代码树的 ext/misc/series.c 文件中) 使用以下架构实现同名虚拟表:

CREATE TABLE generate_series(
  value,
  start HIDDEN,
  stop HIDDEN,
  step HIDDEN
);

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

SELECT value FROM generate_series(5,50);

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

SELECT value FROM generate_series WHERE start=5 AND stop=50;

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

2.1.3. WITHOUT ROWID 虚拟表

从 SQLite 版本 3.14.0 (2016-08-08) 开始, CREATE TABLE 语句 传递到 sqlite3_declare_vtab() 中可以包含 WITHOUT ROWID 子句。 这对于虚拟表行 不容易映射到唯一整数。创建表 包含 WITHOUT ROWID 的语句必须将一个或多个列定义为 主键。PRIMARY KEY 的每一列都必须单独 NOT NULL,并且每行的所有列必须集体唯一。

请注意,SQLite 不会强制执行 WITHOUT ROWID 的 PRIMARY KEY 虚拟表。执行是底层的责任 虚拟表实现。但是 SQLite 确实假设 PRIMARY KEY constraint 是有效的 - 识别的列确实是唯一的,并且 NOT NULL - 它使用该假设来优化针对 虚拟表。

rowid 列在 没有 ROWID 虚拟表(当然)。

xUpdate 方法最初是围绕将 ROWID 作为单个值而设计的。xUpdate 方法已扩展到 容纳任意 PRIMARY KEY 来代替 ROWID,但 PRIMARY KEY 仍只能是一列。出于这个原因,SQLite 将拒绝任何具有多个 WITHOUT ROWID 的虚拟表 PRIMARY KEY 列和非 NULL xUpdate 方法。

2.2. xConnect 方法

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

xConnect 方法与 xCreate 非常相似。 它具有相同的参数并构造新的sqlite3_vtab结构 就像 xCreate 一样。 它还必须像 xCreate 一样调用 sqlite3_declare_vtab()。它 还应该进行所有相同的 sqlite3_vtab_config() 调用,因为 xCreate。

不同的是,调用 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() 释放空间 已报告给应用程序。

每个虚拟表实现都需要 xConnect 方法, 通过 sqlite3_module 对象的 xCreate 和 xConnect 指针 如果虚拟表不需要,则可以指向相同的函数 初始化后备存储。

2.3. xBestIndex 方法

SQLite 使用虚拟表模块的 xBestIndex 方法来确定 访问虚拟表的最佳方式。 xBestIndex 方法有一个原型,如下所示:

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

SQLite 核心通过填充与 xBestIndex 方法进行通信 在sqlite3_index_info结构的某些字段中,并通过 指向该结构的指针,作为第二个参数进入 xBestIndex。 xBestIndex 方法填充此结构的其他字段,这些字段 形成回复。sqlite3_index_info结构如下所示:

struct sqlite3_index_info {
  /* Inputs */
  const int nConstraint;     /* Number of entries in aConstraint */
  const struct sqlite3_index_constraint {
     int iColumn;              /* Column constrained.  -1 for ROWID */
     unsigned char op;         /* Constraint operator */
     unsigned char usable;     /* True if this constraint is usable */
     int iTermOffset;          /* Used internally - xBestIndex should ignore */
  } *const aConstraint;      /* Table of WHERE clause constraints */
  const int nOrderBy;        /* Number of terms in the ORDER BY clause */
  const struct sqlite3_index_orderby {
     int iColumn;              /* Column number */
     unsigned char desc;       /* True for DESC.  False for ASC. */
  } *const aOrderBy;         /* The ORDER BY clause */

  /* Outputs */
  struct sqlite3_index_constraint_usage {
    int argvIndex;           /* if >0, constraint is part of argv to xFilter */
    unsigned char omit;      /* Do not code a test for this constraint */
  } *const aConstraintUsage;
  int idxNum;                /* Number used to identify the index */
  char *idxStr;              /* String, possibly obtained from sqlite3_malloc */
  int needToFreeIdxStr;      /* Free idxStr using sqlite3_free() if true */
  int orderByConsumed;       /* True if output is already ordered */
  double estimatedCost;      /* Estimated cost of using this index */
  /* Fields below are only available in SQLite 3.8.2 and later */
  sqlite3_int64 estimatedRows;    /* Estimated number of rows returned */
  /* Fields below are only available in SQLite 3.9.0 and later */
  int idxFlags;              /* Mask of SQLITE_INDEX_SCAN_* flags */
  /* Fields below are only available in SQLite 3.10.0 and later */
  sqlite3_uint64 colUsed;    /* Input: Mask of columns used by statement */
};

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

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

#define SQLITE_INDEX_CONSTRAINT_EQ         2
#define SQLITE_INDEX_CONSTRAINT_GT         4
#define SQLITE_INDEX_CONSTRAINT_LE         8
#define SQLITE_INDEX_CONSTRAINT_LT        16
#define SQLITE_INDEX_CONSTRAINT_GE        32
#define SQLITE_INDEX_CONSTRAINT_MATCH     64
#define SQLITE_INDEX_CONSTRAINT_LIKE      65  /* 3.10.0 and later */
#define SQLITE_INDEX_CONSTRAINT_GLOB      66  /* 3.10.0 and later */
#define SQLITE_INDEX_CONSTRAINT_REGEXP    67  /* 3.10.0 and later */
#define SQLITE_INDEX_CONSTRAINT_NE        68  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_ISNOT     69  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_ISNOTNULL 70  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_ISNULL    71  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_IS        72  /* 3.21.0 and later */
#define SQLITE_INDEX_CONSTRAINT_LIMIT     73  /* 3.38.0 and later */
#define SQLITE_INDEX_CONSTRAINT_OFFSET    74  /* 3.38.0 and later */
#define SQLITE_INDEX_CONSTRAINT_FUNCTION 150  /* 3.25.0 and later */
#define SQLITE_INDEX_SCAN_UNIQUE           1  /* Scan visits at most 1 row */

使用 sqlite3_vtab_collation() 接口查找 计算第 i 次时应使用的排序规则 约束:

const char *sqlite3_vtab_collation(sqlite3_index_info*, int i);

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

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

在调用此方法之前,SQLite 核心会初始化实例 的sqlite3_index_info结构,以及有关 它当前正在尝试处理的查询。此信息 主要派生自 WHERE 子句和 ORDER BY 或 GROUP BY 子句 ,如果查询是 加入。SQLite 核心提供给 xBestIndex 的信息 方法保存在结构中标记为“输入”的部分。 “输出”部分初始化为零。

sqlite3_index_info结构中的信息是短暂的 并且可能会在 xBestIndex 方法后立即被覆盖或解除分配 返回。如果 xBestIndex 方法需要记住sqlite3_index_info结构的任何部分,则应创建副本。必须小心 将副本存储在将要解除分配的地方,例如 如在 idxStr 字段中,needToFreeIdxStr 设置为 1。

请注意,xBestIndex 将始终在 xFilter 之前调用,因为 xBestIndex 的 idxNum 和 idxStr 输出是 x过滤器。但是,不能保证会调用 xFilter 在成功的 xBestIndex 之后。

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

2.3.1. 输入

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

每个约束通常对应于 WHERE 子句中的一个项 或 USING 或 ON 子句,其形式为

column OP EXPR

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

a = 5

然后,其中一个约束将在“a”列上,其中包含 运算符 “=” 和 “5” 的表达式。约束不需要有 WHERE 子句的文字表示形式。查询优化器可能 对 WHERE 子句,以便提取尽可能多的约束 尽可能。因此,例如,如果 WHERE 子句包含某些内容 喜欢这个:

x BETWEEN 10 AND 100 AND 999>y

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

x >= 10
x <= 100
y < 999

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

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

FUNCTION( column, EXPR)

在本例中,aConstraint[].op 值与该值相同 由 xFindFunction() 为 FUNCTION 返回。

aConstraint[] 数组包含有关所有约束的信息 适用于虚拟表。但一些限制可能会 由于表在联接中的排序方式而不可用。 因此,xBestIndex 方法必须只考虑约束 具有 aConstraint[].usable 标志,该标志为 true。

除了 WHERE 子句约束之外,SQLite 核心还 告知 xBestIndex 方法有关 ORDER BY 子句的信息。 (在聚合查询中,SQLite 核心可能会放入 GROUP BY 子句 information 代替 ORDER BY 子句信息,但这个事实 应该不会对 xBestIndex 方法产生任何影响。 如果 ORDER BY 子句的所有项都是虚拟表中的列, 则 nOrderBy 将是 ORDER BY 子句中的项数 aOrderBy[] 数组将标识每个术语的列 按子句顺序排列,以及该列是 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(EXPR, column)
GLOB(EXPR, column)
REGEXP(EXPR, column)
MATCH(EXPR, column)

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

column LIKE EXPR
LIKE(EXPR,column)

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

LIKE、GLOB、REGEXP 和 MATCH 的此特殊功能没有 但是,适用于 xFindFunction() 方法。xFindFunction() 方法始终关闭 LIKE、GLOB、REGEXP 或 MATCH 运算符,但不在第一个参数中 到这些运算符的函数调用等效项。

2.3.1.2. LIMIT和OFFSET

当 aConstraint[].op 是 SQLITE_INDEX_CONSTRAINT_LIMIT 或 SQLITE_INDEX_CONSTRAINT_OFFSET,这表明存在一个 LIMIT 或 OFFSET 子句,用于 SQL 查询语句 虚拟表。LIMIT 和 OFFSET 运算符没有 左操作数,因此当 aConstraint[].op 是 SQLITE_INDEX_CONSTRAINT_LIMIT还是SQLITE_INDEX_CONSTRAINT_OFFSET 则 aConstraint[].iColumn 值没有意义,应该 不使用。

2.3.1.3. 约束的右侧值

sqlite3_vtab_rhs_value() 接口可用于尝试 访问约束的右侧操作数。但是,值 右手操作员当时可能不知道 运行 xBestIndex 方法,因此 sqlite3_vtab_rhs_value() 调用可能不成功。通常是 只有当 xBestIndex 编码为 输入 SQL 中的文本值。如果正确的操作数是 编码为表达式或主机参数,它可能会 xBestIndex 无法访问。某些运算符(如 SQLITE_INDEX_CONSTRAINT_ISNULL 和 SQLITE_INDEX_CONSTRAINT_ISNOTNULL)没有右手操作数。 sqlite3_vtab_rhs_value() 接口始终返回此类运算符的SQLITE_NOTFOUND。

2.3.2. 输出

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

xBestIndex 方法通过 idxNum 和 idxStr 字段将索引策略传达给 xFilter 方法。idxNum 值和 就 SQLite 核心而言,idxStr 字符串内容是任意的 相关,只要 xBestIndex 和 xFilter 就可以具有任何含义 就其含义达成一致。SQLite 核心只是复制 从 xBestIndex 到 xFilter 方法的信息,假设 只是通过 idxStr 引用的 char 序列是 NUL 终止的。

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

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

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

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

aConstraintUsage[] 数组包含每个 sqlite3_index_info结构的 inputs 部分中的 nConstraint 约束。 xBestIndex 使用 aConstraintUsage[] 数组来告诉 核心是如何使用约束。

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

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

2.3.2.1. 省略字节码中的约束检查

默认情况下,SQLite 生成的字节码将翻倍 检查虚拟表每行的所有约束以验证 他们满意。如果虚拟表可以保证 约束将始终得到满足,它可以尝试 通过设置 aConstraintUsage[].omit 来禁止该双重检查。 但是,除了一些例外,这只是一个提示和 不能保证约束的冗余检查 将被抑制。要点:

  • 仅当 argvIndex 值为 constraint 大于 0 且小于或等于 16。 从不禁止对约束进行约束检查 不会将其右操作数传递到 xFilter 方法中。 当前的实现只能抑制冗余 对传递给 xFilter 的前 16 个值进行约束检查, 尽管该限制可能会在将来的版本中增加。

  • 只要 argvIndex 大于 0,就会始终为SQLITE_INDEX_CONSTRAINT_OFFSET约束提供省略标志。将 SQLITE_INDEX_CONSTRAINT_OFFSET约束上的省略标志表示 到 SQLite,虚拟表本身将抑制前 N 个 输出行,其中 N 是 OFFSET 运算符的右操作数。 如果虚拟表实现集省略 SQLITE_INDEX_CONSTRAINT_OFFSET约束,但随后无法抑制 前 N 行输出,错误答案将导致 整体查询。

2.3.2.2. ORDER BY和orderByConsumed

如果虚拟表将按 ORDER BY 子句,则 orderByConsumed 标志可以设置为 真。如果输出未自动按正确的顺序输出 则 orderByConsumed 必须保留在其默认的 false 设置中。 这将向 SQLite 核心指示它需要执行 在数据从虚拟表中出来后,对数据进行单独的排序传递。 设置 orderByConsumed 是一种优化。查询将始终 如果 orderByConsumed 保留为默认值,则获得正确答案 值 (0)。可能会避免不必要的排序操作,从而导致 在更快的查询中,如果设置了 orderByConsumeed,但设置 orderByConsumed 不正确可能会导致不正确的答案。 建议新的虚拟表实现离开 orderByConsumed 值最初未设置,然后在所有内容之后未设置 否则已知工作正常,请返回并尝试 通过在适当的情况下设置 orderByConsumed 进行优化。

有时,即使 虚拟表的输出不严格按顺序排列 由 nOrderBy 和 aOrderBy 指定。如果 sqlite3_vtab_distinct() 接口返回 1 或 2,则表示 订购可以放宽。有关详细信息,请参阅 sqlite3_vtab_distinct() 上的文档。

2.3.3. 返回值

xBestIndex 方法应在成功时返回SQLITE_OK。如果有的话 发生致命错误,适当的错误代码(例如:SQLITE_NOMEM) 应改为返回。

如果 xBestIndex 返回 SQLITE_CONSTRAINT,则不表示 错误。相反,SQLITE_CONSTRAINT表示特定的组合 对于虚拟表指定的输入参数是不够的 完成其工作。 这在逻辑上与将 estimatedCost 设置为无穷大相同。 如果对特定查询计划的每次调用 xBestIndex 都返回 SQLITE_CONSTRAINT,这意味着虚拟表没有办法 安全使用,sqlite3_prepare() 调用将失败,并返回 “无查询解决方案”错误。

2.3.4. 对表值函数强制执行必需参数

xBestIndex 的SQLITE_CONSTRAINT回报 对于表值函数很有用 具有必需的参数。如果 aConstraint[].usable 字段为 false 对于必需的参数之一,则 xBestIndex 方法应 返回SQLITE_CONSTRAINT。如果必填字段未出现在 aConstraint[] 数组,这意味着对应的 参数从输入 SQL 中省略。在这种情况下,xBestIndex 应在 pVTab->zErrMsg 中设置错误消息并返回 SQLITE_ERROR。总结一下:

  1. 必需参数的 aConstraint[].usable 值为 false →返回SQLITE_CONSTRAINT。

  2. 必需的参数不会出现在 aConstraint[] 数组→在 pVTab->zErrMsg 中设置错误消息并返回 SQLITE_ERROR

以下示例将更好地说明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. 扫描 tablevalued func 的所有行,并为每一行查找行 在 Realtab 中,其中 x 等于 tablevaluedfunc.param1。

对于每个电位,将调用一次 xBestIndex 方法 上面的计划。对于计划 1,aConstraint[].usable 标志为 param1 列的SQLITE_CONSTRAINT_EQ约束将为 true,因为 “param1 = ?”约束的右侧值是已知的, 因为它是由外部 RealTab 循环决定的。 但是对于计划 2,“param1 = ?”的 aConstraint[].usable 标志将为 false 因为右侧值是由内部循环确定的,因此 一个未知的数量。因为 param1 是表值的必需输入 函数,xBestIndex 方法在显示时应返回SQLITE_CONSTRAINT 使用计划 2,指示缺少所需的输入。这迫使 查询 Planner 以选择计划 1。

2.4. xDisconnect 方法

int (*xDisconnect)(sqlite3_vtab *pVTab);

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

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

每个虚拟表实现都需要 xDisconnect 方法, 尽管 xDisconnect 和 xDestroy 方法是可以接受的 如果对特定虚拟表有意义,则使用相同的函数。

2.5. xDestroy 方法

int (*xDestroy)(sqlite3_vtab *pVTab);

此方法释放与虚拟表的连接,就像 xDisconnect 方法,它还破坏了底层 表实现。此方法将撤消 xCreate 的工作。

每当数据库连接时,都会调用 xDisconnect 方法 使用虚拟表的已关闭。xDestroy 方法仅 在对虚拟表执行 DROP TABLE 语句时调用。

每个虚拟表实现都需要 xDestroy 方法, 尽管 xDisconnect 和 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 打开的游标。 SQLite 核心将始终为打开的每个游标调用 xClose 一次 使用 xOpen。

此方法必须释放 对应的 xOpen 调用。即使例程不会再次调用 返回错误。关闭后,SQLite 核心将不再使用sqlite3_vtab_cursor。

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

2.8. xEof 方法

int (*xEof)(sqlite3_vtab_cursor*);

如果指定的游标 currently 指向有效的数据行,否则为 true(非零)。 SQL 引擎在每次 xFilter 和 xNext 调用后立即调用此方法。

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

2.9. xFilter 方法

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

此方法开始搜索虚拟表。 第一个参数是由 xOpen 打开的游标。 接下来的两个参数定义了前面的特定搜索索引 由 xBestIndex 选择。idxNum 和 idxStr 的具体含义 只要 xFilter 和 xBestIndex 在什么上达成一致,就不重要 这个意思就是。

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

如果虚拟表包含一行或多行,这些行与 搜索条件,则光标必须左指向第一行。 对 xEof 的后续调用必须返回 false(零)。 如果没有匹配的行,则光标必须保持状态 这将导致 xEof 返回 true(非零)。 SQLite 引擎将使用 用于访问该行内容的 xColumn 和 xRowid 方法。 xNext 方法将用于前进到下一行。

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

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

2.10. xNext方法

int (*xNext)(sqlite3_vtab_cursor*);

xNext 方法将虚拟表游标前进到由 xFilter 启动的结果集的下一行。 如果光标已经指向最后一行,则当 调用例程,则光标不再指向有效 data 和对 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: 以下接口:

  • sqlite3_result_blob()
  • sqlite3_result_double()
  • sqlite3_result_int()
  • sqlite3_result_int64()
  • sqlite3_result_null()
  • sqlite3_result_text()
  • sqlite3_result_text16()
  • sqlite3_result_text16le()
  • sqlite3_result_text16be()
  • sqlite3_result_zeroblob()

如果 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,对于插入操作,其值为 N+2 或替换或更新,其中 N 是表中的列数。 在上一句中,N 包括任何隐藏列。

每个 argv 条目在 C 中都有一个非 NULL 值,但可能包含 SQL 值 NULL。换句话说,对于介于 0 和 argc-1 之间的 i 来说,argv[i]!=0 始终是正确的。 但是,可能是 sqlite3_value_type(argv[i])==SQLITE_NULL.

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

argv[1] 参数是要插入的新行的 rowid 到虚拟表中。如果 argv[1] 是 SQL NULL,则实现 必须为新插入的行选择 rowid。后续 argv[] 条目包含虚拟表的列值,在 声明列的顺序。列数将 匹配 xConnect 或 xCreate 方法所做的表声明 使用 sqlite3_declare_vtab() 调用。所有隐藏的列都包括在内。

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

每次调用 xUpdate 都属于如下所示的情况之一。 并不是说对 argv[i] 的引用意味着 SQL 值 保存在 argv[i] 对象中,而不是 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。argv[1] 将为 NULL 对于 WITHOUT ROWID 虚拟表,在这种情况下,实现应 从 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 时,如语句所示:

UPDATE table SET rowid=rowid+1 WHERE ...;

xUpdate 方法必须返回 SQLITE_OK 当且仅当它是 成功的。如果发生故障,xUpdate 必须返回相应的错误代码。失败时,pVTab->zErrMsg 元素可以选择 替换为存储在从SQLite分配的内存中的错误消息文本 使用 sqlite3_mprintf() 或 sqlite3_malloc() 等函数。

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

如果 xUpdate 方法正在执行 UPDATE,则 sqlite3_value_nochange(X) 可用于发现哪些列 的虚拟表实际上被 UPDATE 修改了 陈述。sqlite3_value_nochange(X) 接口返回 对于不更改的列,为 true。 在每次更新时,SQLite 将首先为表中每个不变的列单独调用 xColumn,以 获取该列的值。xColumn 方法可以 检查该列在 SQL 级别是否未更改 通过调用 sqlite3_vtab_nochange()。如果 xColumn 看到 列未被修改,它应该返回而不设置 使用 sqlite3_result_xxxxx() 接口之一的结果。只有在这种情况下,sqlite3_value_nochange() 才会 在 xUpdate 方法中为 true。如果 xColumn 这样做 调用一个或多个 sqlite3_result_xxxxx() 接口,然后 SQLite 将其理解为值的变化 的列和 sqlite3_value_nochange() 调用 xUpdate 中的列将返回 false。

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

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

2.14. xFindFunction 方法

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

在 sqlite3_prepare() 期间调用此方法以给出虚拟 表实现是重载函数的机会。 此方法可以设置为 NULL,在这种情况下不会发生重载。

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

从历史上看,xFindFunction()的返回值为零 或一个。零表示函数没有重载,一表示 这是超载。在 中添加了返回 SQLITE_INDEX_CONSTRAINT_FUNCTION 或更大值的功能 版本 3.25.0 (2018-09-15).如果 xFindFunction 返回SQLITE_INDEX_CONSTRAINT_FUNCTION或大于,则表示该函数 接受两个参数和函数 可以用作查询的 WHERE 子句中的布尔值,并且 虚拟表能够利用该函数来加快查询速度 结果。当 xFindFunction 返回 SQLITE_INDEX_CONSTRAINT_FUNCTION 或 越大,返回的值将成为 sqlite3_index_info.aConstraint.op 传递给 xBestIndex() 的约束之一的值。第一个 函数的参数是 aConstraint[].iColumn 字段和 function 是将传递给 xFilter() 的值(如果 aConstraintUsage[].argvIndex 值)或从 sqlite3_vtab_rhs_value() 返回的值。

Geopoly 模块是使用 SQLITE_INDEX_CONSTRAINT_FUNCTION提高性能。 Geopoly 返回的 xFindFunction() 方法 geopoly_overlap() SQL 函数的SQLITE_INDEX_CONSTRAINT_FUNCTION 它回来了 SQLITE_INDEX_CONSTRAINT_FUNCTION+1 表示 geopoly_within() SQL 函数。 这允许对查询进行搜索优化,例如:

SELECT * FROM geopolytab WHERE geopoly_overlap(_shape, $query_polygon);
SELECT * FROM geopolytab WHERE geopoly_within(_shape, $query_polygon);

请注意,中缀函数(LIKE、GLOB、REGEXP和MATCH)反转 他们论点的顺序。因此,“like(A,B)”通常工作相同 作为“B like A”。 但是,xFindFunction() 看起来总是最左边的参数,而不是 第一个逻辑论证。 因此,对于“B like A”的形式,SQLite 着眼于 左操作数“B”,如果该操作数是虚拟表列 它调用该虚拟表上的 xFindFunction() 方法。 但是,如果改用“like(A,B)”形式,则SQLite会检查 A 项,用于查看它是否是虚拟表的列,如果是 它调用 xFindFunction() 方法的虚拟表 A列。

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

2.15. xBegin 方法

int (*xBegin)(sqlite3_vtab *pVTab);

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

此方法后面始终跟着对 xCommit 或 xRollback 方法的一次调用。虚拟表事务执行 不是嵌套,因此不会多次调用 xBegin 方法 在单个虚拟表上 无需对 xCommit 或 xRollback 进行干预调用。 对其他方法的多次调用可能而且可能会在两者之间发生 xBegin 和相应的 xCommit 或 xRollback。

2.16. xSync 方法

int (*xSync)(sqlite3_vtab *pVTab);

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

此方法仅在调用 xBegin 方法和 在 xCommit 或 xRollback 之前。为了实现两阶段 commit,则在以下日期之前调用所有虚拟表上的 xSync 方法 在任何虚拟表上调用 xCommit 方法。如果 xSync 方法失败,整个事务将回滚。

2.17. xCommit 方法

int (*xCommit)(sqlite3_vtab *pVTab);

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

对此方法的调用始终遵循对 xBegin 和 xSync 的先前调用。

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是 off,则将每隔一次为虚拟表调用 xConnect 方法 xRename 方法尝试更改影子表的名称的时间。

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。 后续调用 to 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 方法

一些虚拟表实现(例如:FTS3、FTS5 和 RTREE)使 使用真实(非虚拟)数据库表来存储内容。例如 当内容插入到 FTS3 虚拟表中时,数据最终是 存储在名为 “%_content”、“%_segdir”、“%_segments”、“%_stat” 的实际表中, 和 “%_docsize”,其中 “%” 是原始虚拟表的名称。这 存储虚拟表内容的辅助实表称为 “影子表”。看 (1), (2)和 (3) 更多信息。

xShadowName 方法的存在是为了允许 SQLite 确定 某些真实表实际上是虚拟表的影子表。

SQLite将真实表理解为影子表,如果所有 以下情况是正确的:

  • 表的名称包含一个或多个“_”字符。
  • 名称中最后一个“_”之前的部分完全匹配 使用 CREATE VIRTUAL TABLE 创建的虚拟表的名称。 (对于同名虚拟表和表值函数,无法识别影子表。
  • 虚拟表包含一个 xShadowName 方法。
  • 当 xShadowName 方法的输入是部件时,xShadowName 方法返回 true 表名,超过最后一个“_”字符。

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

xShadowName 方法的全部意义在于保护 影子表被恶意 SQL 破坏。每个虚拟表 使用影子表的实现应该能够检测和应对 具有损坏的影子表内容。但是,特别是虚拟的错误 表实现可能允许故意损坏的影子表 导致崩溃或其他故障。xShadowName 机制寻求 通过防止普通 SQL 语句 故意损坏影子表。

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

2.22. xIntegrity 方法

如果sqlite3_module的 iVersion 为 4 或更多,并且 xIntegrity method 不为 NULL,则 PRAGMA integrity_check 和 PRAGMA quick_check 命令将调用 xIntegrity 作为其处理的一部分。如果 xIntegrity 方法写入 将错误消息字符串添加到第五个参数中,然后 PRAGMA integrity_check 将该错误作为其输出的一部分报告。所以,换句话说, xIntegrity 方法允许 PRAGMA integrity_check 命令验证 存储在虚拟表中的内容的完整性。

xIntegrity 方法使用五个参数调用:

  • pVTab → 指向 sqlite3_vtab 对象的指针,该对象是 正在检查的虚拟表。
  • zSchema → 架构的名称(“main”、“temp”等),其中 虚拟表已定义。
  • zTabName → 虚拟表的名称。
  • mFlags →指示这是否是“integrity_check”的标志 或“quick_check”。目前,此参数将始终为 0 或 1, 尽管 SQLite 的未来版本可能会使用整数的其他位来 指示其他处理选项。
  • pzErr → 此参数指向已初始化的“char*” 更改为 NULL。xIntegrity()实现应使 *pzErr 指向 从 sqlite3_malloc()或等效项获取的错误字符串(如果找到 任何问题。

xIntegrity 方法通常应返回 SQLITE_OK - 即使它找到 虚拟表内容中的问题。任何其他错误代码 表示 xIntegrity 方法本身在尝试时遇到了问题 以评估虚拟表内容。因此,例如,如果倒置 发现 FTS5 的索引内部不一致,则 xIntegrity 方法应将适当的错误消息写入 pzErr 参数 并返回SQLITE_OK。但是,如果 xIntegrity 方法无法完成其 由于内存不足而对虚拟表内容进行评估,然后 它应该返回SQLITE_NOMEM。

如果生成错误消息, 保存错误消息字符串的空间应从 sqlite3_malloc64() 或等效项中获取。错误消息字符串的所有权将传递给 当 xIntegrity 返回时,SQLite 核心。核心将确保调用 sqlite3_free() 来回收它已完成的内存 替换为错误消息。PRAGMA integrity_check命令调用 xIntegrity 方法不会更改返回的错误消息。xIntegrity 方法本身应包含虚拟表的名称,作为 消息。提供了 zSchema 和 zName 参数以简化此操作。

mFlags 参数当前是一个布尔值(0 或 1),表示 如果由于 PRAGMA integrity_check (mFlags==0) 而调用了 xIntegrity 方法 或由于 PRAGMA quick_check (mFlags==1)。一般来说,xIntegrity 方法应该在线性时间内完成任何有效性检查 无论如何,但只有在 (mFlags&1)==0 时才进行需要超线性时间的检查。SQLite 的未来版本可能 使用 mFlags 参数的高阶位来指示其他 处理选项。

支持 在 SQLite 版本 3.44.0 (2023-11-01) 中添加了 xIntegrity 方法。 在同一版本中,xIntegrity 方法被添加到许多内置 虚拟表,如 FTS3、FTS5 和 RTREE,以便内容 此后,将自动检查这些表的一致性 当 PRAGMA integrity_check运行时。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/524741.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

目标跟踪——行人检测数据集

一、重要性及意义 目标跟踪和行人检测是计算机视觉领域的两个重要任务&#xff0c;它们在许多实际应用中发挥着关键作用。为了推动这两个领域的进步&#xff0c;行人检测数据集扮演着至关重要的角色。以下是行人检测数据集的重要性及意义的详细分析&#xff1a; 行人检测数据…

MySQL操作DML

目录 1.概述 2.插入 3.更新 4.删除 5.查询 6.小结 1.概述 数据库DML是数据库操作语言&#xff08;Data Manipulation Language&#xff09;的简称&#xff0c;主要用于对数据库中的数据进行增加、修改、删除等操作。它是SQL语言的一部分&#xff0c;用于实现对数据库中数…

python统计分析——多组比较

参考资料&#xff1a;python统计分析【托马斯】 一、方差分析 1、原理 方差分析的思想是将方差分为组间方差和组内方差&#xff0c;看看这些分布是否符合零假设&#xff0c;即所有组都来自同一分布。区分不同群体的变量通常被称为因素或处理。 作为对比&#xff0c;t检验观察…

(React Hooks)前端八股文修炼Day9

一 对 React Hook 的理解&#xff0c;它的实现原理是什么 React Hooks是React 16.8版本中引入的一个特性&#xff0c;它允许你在不编写类组件的情况下&#xff0c;使用state以及其他的React特性。Hooks的出现主要是为了解决类组件的一些问题&#xff0c;如复杂组件难以理解、难…

科技云报道:卷完参数卷应用,大模型落地有眉目了?

科技云报道原创。 国内大模型战场的比拼正在进入新的阶段。 随着产业界对模型落地的态度逐渐回归理性&#xff0c;企业客户的认知从原来的“觉得大模型什么都能做”的阶段&#xff0c;已经收敛到“大模型能够给自身业务带来什么价值上了”。 2023 年下半年&#xff0c;不少企…

Zotero参考文献引用(适用国内)

配置环境 【1】Windows 10 【2】Office 2021 【3】Zotero 6 目录 配置环境 前言 一、软件及插件安装 二、网页信息抓取 三、文献引用 注意事项 前言 本教程记录使用zotero作为自动插入参看文献引用工具的全流程&#xff0c;流程较为简洁&#xff0c;适用于平时论文写作不多&…

C语言 | Leetcode C语言题解之第15题三数之和

题目&#xff1a; 题解&#xff1a; int cmp(const void *x, const void *y) {return *(int*)x - *(int*)y; } //判断重复的三元组 bool TheSame(int a, int b, int c, int **ans, int returnSize) {bool ret true;for(int i 0;i < returnSize;i){if(a ans[i][0] &&…

书生·浦语训练营二期第三次笔记-茴香豆:搭建你的 RAG 智能助理

RAG学习文档1&#xff1a; https://paragshah.medium.com/unlock-the-power-of-your-knowledge-base-with-openai-gpt-apis-db9a1138cac4 RAG学习文档2: https://blog.demir.io/hands-on-with-rag-step-by-step-guide-to-integrating-retrieval-augmented-generation-in-llms-a…

2024/4/1—力扣—主要元素

代码实现&#xff1a; 思路&#xff1a;摩尔投票算法 int majorityElement(int *nums, int numsSize) {int candidate -1;int count 0;for (int i 0; i < numsSize; i) {if (count 0) {candidate nums[i];}if (nums[i] candidate) {count;} else {count--;}}count 0;…

操作系统复习

虚拟内存 内存(memory)资源永远都是稀缺的&#xff0c;当越来越多的进程需要越来越来内存时&#xff0c;某些进程会因为得不到内存而无法运行&#xff1b; 虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存&#xff0c;而实际上&#xff0…

网络与并发编程(一)

并发编程介绍_串行_并行_并发的区别 串行、并行与并发的区别 串行(serial)&#xff1a;一个CPU上&#xff0c;按顺序完成多个任务并行(parallelism)&#xff1a;指的是任务数小于等于cpu核数&#xff0c;即任务真的是一起执行的并发(concurrency)&#xff1a;一个CPU采用时间…

JVM虚拟机(一)介绍、JVM组成、堆、栈、方法区/元空间、直接内存

目录 一、JVM 介绍1.1 为什么要学 JVM&#xff1f;1.2 JVM 是什么&#xff1f; 二、JVM 组成2.1 程序计数器2.2 Java堆1&#xff09;JVM 内存结构2&#xff09;Java 1.7 和 1.8 中堆的区别 2.3 Java虚拟机栈1&#xff09;虚拟机栈 和 栈帧2&#xff09;常见面试题 2.4 方法区/元…

Transformer模型-Feed Forward前馈网络和Relu激活函数的简明介绍

今天介绍transformer模型的Feed Forward network前馈网络和Relu激活函数 背景 位置感知Position-Wise前馈网络&#xff08;FFN&#xff09;由两个全连接层&#xff08;fully connected dense layers&#xff0c;就是线性层&#xff08;Linear Layer&#xff09;&#xff0c;或密…

LLM-base版本和chat版本的比较

突然想到了这个问题&#xff0c;网上搜集了一些资料&#xff0c;自己也总结一下 首先放一张llama2论文当中的图&#xff0c;可以很直观的看到区别 面试回答版 问题&#xff1a; 大语言模型base版和chat版的区别是什么&#xff1f; 回答&#xff1a; base版本更适合文本补全…

医疗器械FDA | 常见的网络安全材料发补问题都有哪些?

FDA网络安全资料发补咨询点此​​获取https://work.weixin.qq.com/ca/cawcde5ee29d239046 ————————--- 01 安全文档编写问题 FDA网络安全文档编写格式、内容、可读性等未满足官方要求&#xff0c;则将可能被要求发补整改编写后的文档。 02 安全管理问题 a. 网络安…

Apache-Pulsar安装操作说明

说明 Pulsar 是一种用于服务器到服务器消息传递的多租户高性能解决方案。 Pulsar 的主要特性如下&#xff1a; 对 Pulsar 实例中的多个集群的本机支持&#xff0c;并跨集群无缝地复制消息。 极低的发布和端到端延迟。 无缝可扩展至超过一百万个主题。 一个简单的客户端 API&…

C语言--#运算符和##运算符

#运算符和##运算符 这里提前补充一点printf C语言里面会天然的吧printf里面俩字符串合并为一个 #的使用 在C语言中&#xff0c;#符号主要用于预处理器指令。预处理器是在代码编译之前执行的&#xff0c;它处理所有以#开始的指令。以下是一些常见的使用情况&#xff1a;1. **…

自定义校验器

1.前端校验 <template><el-dialog:title"!dataForm.brandId ? 新增 : 修改":close-on-click-modal"false":visible.sync"visible"><el-form:model"dataForm":rules"dataRule"ref"dataForm"keyu…

MySQL-主从复制:概述、原理、同步数据一致性问题、搭建流程

主从复制 1. 主从复制概述 1.1 如何提升数据库并发能力 一般应用对数据库而言都是“读多写少”&#xff0c;也就说对数据库读取数据的压力比较大&#xff0c;有一个思路就是采用数据库集群的方案&#xff0c;做主从架构、进行读写分离&#xff0c;这样同样可以提升数据库的并…

基于R语言、MaxEnt模型融合技术的物种分布模拟、参数优化方法、结果分析制图与论文写作

第一章、理论篇&#xff1a;以问题导入的方式&#xff0c;深入掌握原理基础 什么是MaxEnt模型&#xff1f; MaxEnt模型的原理是什么&#xff1f;有哪些用途&#xff1f; MaxEnt运行需要哪些输入文件&#xff1f;注意那些事项&#xff1f; 融合R语言的MaxEnt模型的优势&…