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

本文围绕SQLite虚拟表展开,介绍了其用法,如通过CREATE VIRTUAL TABLE语句创建,存在临时、同名等类型;阐述了实现所需的C级对象及注册方法;还提及虚拟表和共享缓存的关系。此外,详细讲解了虚拟表的多种方法,如xCreate、xConnect等。

返回:SQLite—系列文章目录   

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

下一篇:SQLite数据库文件格式(十五)

1. 引言

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

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

单个虚拟表实现可能会施加额外的 约束。例如,某些虚拟实现可能提供 只读表。或者,某些虚拟表实现可能允许 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 源代码树包含许多适合复制的虚拟表实现, 包括:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

界忆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值