database.createModule(name, options)


  • name <string> 虚拟表模块的名称。这个名称用于 CREATE VIRTUAL TABLE ... USING name 语句中,并且用作同名表的名称。
  • options <Object> 模块配置设置。
    • columns <Array> 一组列定义数组。每个元素都是一个对象,具有以下属性:
      • name <string> 这一列的名字。
      • type <string> 列的声明类型。必须是 'INTEGER'、'TEXT'、'REAL'、'BLOB' 或 'ANY' 中的一个。
      • hidden <boolean> 如果 true,该列将被隐藏,并作为表值函数使用的参数。默认值: false。
    • rows <Function> 当虚拟表被查询时,会调用一个函数来生成行。该函数会接收隐藏列(参数)的值作为参数,顺序按照它们被定义的顺序。必须返回一个可迭代对象(比如数组或生成器),其中每个元素都是一组列值数组。
    • directOnly <boolean> 如果 true,虚拟表只能在顶层 SQL 语句中使用,不能在触发器或视图中使用。 默认值: false。
    • useBigIntArguments <boolean> 如果 true,传递给 rows 的整数参数会被转换为 BigInt。默认值: false。

在数据库中注册一个虚拟表模块。这个方法是 sqlite3_create_module_v2() 的一个封装。虚拟表允许 JavaScript 代码为 SQL 表提供后台数据。注册的模块可以有两种使用方式:

🌐 Registers a virtual table module with the database. This method is a wrapper around sqlite3_create_module_v2(). Virtual tables allow JavaScript code to provide the backing data for SQL tables. The registered module can be used in two ways:

  • 同名表:直接查询模块名称而不创建表(例如,SELECT * FROM module_name)。
  • 命名虚拟表:使用 CREATE VIRTUAL TABLE t USING module_name 来创建一个持久的虚拟表。

隐藏列可以用来通过表值函数语法(例如 SELECT * FROM module_name(param1, param2))向 rows 函数传递参数。

🌐 Hidden columns can be used to pass parameters to the rows function using table-valued function syntax (e.g., SELECT * FROM module_name(param1, param2)).

rows 产生的值遵循 JavaScript 和 SQLite 之间的类型转换 中的转换规则:一个 <number> 被存储为 REAL,一个 <bigint> 被存储为 INTEGER,不管列声明的 type 是什么。和普通表不一样,虚拟表不会对返回的值应用列类型偏好,所以在某列需要 INTEGER 存储时,返回一个 <bigint>:

🌐 Values yielded by rows follow the conversion rules in Type conversion between JavaScript and SQLite: a <number> is stored as REAL and a <bigint> is stored as INTEGER, regardless of the column's declared type. Unlike an ordinary table, a virtual table does not apply column affinity to the values it returns, so yield a <bigint> when a column needs INTEGER storage:

db.createModule('counter', {
  columns: [{ name: 'value', type: 'INTEGER' }],
  *rows() {
    yield [1];   // typeof(value) is 'real'
    yield [2n];  // typeof(value) is 'integer'
  },
}); 
const { Database } = require('node:sqlite');

const db = new Database(':memory:');

db.createModule('generate_series', {
  columns: [
    { name: 'value', type: 'INTEGER' },
    { name: 'start', type: 'INTEGER', hidden: true },
    { name: 'stop', type: 'INTEGER', hidden: true },
    { name: 'step', type: 'INTEGER', hidden: true },
  ],
  *rows(start, stop, step) {
    start ??= 0;
    stop ??= 10;
    step ??= 1;
    for (let i = start; i <= stop; i += step) {
      yield [i];
    }
  },
});

console.log(db.prepare('SELECT * FROM generate_series(1, 5, 1)').all());
// Prints: [ { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, { value: 5 } ]import { Database } from 'node:sqlite';

const db = new Database(':memory:');

db.createModule('generate_series', {
  columns: [
    { name: 'value', type: 'INTEGER' },
    { name: 'start', type: 'INTEGER', hidden: true },
    { name: 'stop', type: 'INTEGER', hidden: true },
    { name: 'step', type: 'INTEGER', hidden: true },
  ],
  *rows(start, stop, step) {
    start ??= 0;
    stop ??= 10;
    step ??= 1;
    for (let i = start; i <= stop; i += step) {
      yield [i];
    }
  },
});

console.log(db.prepare('SELECT * FROM generate_series(1, 5, 1)').all());
// Prints: [ { value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }, { value: 5 } ]