From b6d814aefe98ff196de256de52115bd0ff547e5a Mon Sep 17 00:00:00 2001 From: thzxx Date: Sun, 26 Jul 2026 16:34:34 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20README=20+=20site=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=20v0.1.13=20=E6=96=B0=E7=89=B9=E6=80=A7=EF=BC=88=E4=BA=8B?= =?UTF-8?q?=E5=8A=A1=E5=9B=9E=E6=BB=9A/=E5=AD=90=E6=9F=A5=E8=AF=A2/?= =?UTF-8?q?=E5=A4=96=E9=94=AE=E7=BA=A7=E8=81=94/=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=B1=A0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 80 ++++++++++++++++++++++++++++++++++------------ site/demo.html | 19 +++++++++-- site/docs.html | 85 +++++++++++++++++++++++++++++++++++++++++++------ site/index.html | 62 +++++++++++++++++++++++++----------- 4 files changed, 197 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index bda6d22..d5b7878 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MetonaSqlark

- version + version license coverage tests @@ -11,6 +11,15 @@ --- +## ✨ v0.1.13 新特性 + +- 🔒 **事务回滚** — `beginTransaction/commitTransaction/rollbackTransaction` 真正的原子操作 +- 🔍 **子查询** — `IN (SELECT ...)` 和标量子查询 `= (SELECT ...)` +- 🔗 **外键级联** — `ON DELETE CASCADE / SET NULL / RESTRICT` 递归级联删除 +- 🏊 **连接池** — `MetonaSqlark.connect()` 单例复用,引用计数管理 + +--- + ## 📦 安装 ```bash @@ -72,15 +81,6 @@ const { MetonaSqlark } = require('@metona-team/metona-sqlark'); ``` -### ES Module in Browser - -```html - -``` - --- ## 🚀 快速开始 @@ -94,7 +94,7 @@ const db = await MetonaSqlark.create({ diskEngine: 'indexeddb', // 'indexeddb' | 'opfs' }); -// 定义表 +// 定义表 — 支持外键级联 await db.defineTable('users', { id: { type: 'string', primaryKey: true }, name: { type: 'string', required: true }, @@ -102,6 +102,12 @@ await db.defineTable('users', { email: { type: 'string', unique: true, index: true }, }); +await db.defineTable('orders', { + id: { type: 'string', primaryKey: true }, + user_id: { type: 'string', references: 'users.id', onDelete: 'CASCADE' }, + amount: { type: 'number' }, +}); + // SQL 查询 await db.query("INSERT INTO users VALUES ('1', 'Alice', 30, 'alice@demo.com')"); const rows = await db.query('SELECT * FROM users WHERE age > 18 ORDER BY name'); @@ -117,14 +123,23 @@ const result = await db.table('users') // JOIN await db.query(`SELECT u.name, o.product FROM users u INNER JOIN orders o ON u.id = o.user_id`); +// 子查询 v0.1.13 +await db.query(`SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`); + // GROUP BY await db.query(`SELECT dept, COUNT(*) FROM employees GROUP BY dept HAVING COUNT(*) > 1`); -// 事务 +// 事务 — 自动回滚 v0.1.13 await db.transaction(async (trx) => { await trx.table('users').insert({ id: '3', name: 'Charlie' }); await trx.table('orders').insert({ id: 'o1', userId: '3', amount: 99 }); + // 任何一步失败 → 全部回滚 }); + +// 连接池 v0.1.13 +const db2 = await MetonaSqlark.connect({ name: 'my-app', mode: 'hybrid' }); +// db2 === db(复用已有实例) +await db2.disconnect(); // 引用计数 -1 ``` --- @@ -140,6 +155,20 @@ await db.transaction(async (trx) => { | `diskEngine` | `'indexeddb' \| 'opfs'` | `'indexeddb'` | 磁盘引擎 | | `version` | `number` | `1` | 版本号 | +### ColumnDef 列定义 + +| 属性 | 类型 | 说明 | +|------|------|------| +| `type` | `'string'\|'number'\|'boolean'\|'date'\|'json'` | 数据类型 | +| `primaryKey` | `boolean` | 主键 | +| `required` | `boolean` | 必填 | +| `unique` | `boolean` | 唯一约束 | +| `index` | `boolean` | 创建哈希索引 | +| `default` | `unknown` | 默认值 | +| `references` | `string` | 外键引用 `'table.column'` | +| `onDelete` | `'CASCADE'\|'SET NULL'\|'RESTRICT'` | 删除级联 🆕 | +| `onUpdate` | `'CASCADE'\|'SET NULL'\|'RESTRICT'` | 更新级联 🆕 | + ### WHERE 操作符 | 操作符 | 含义 | 操作符 | 含义 | @@ -156,12 +185,21 @@ await db.transaction(async (trx) => { | `db.query(sql)` | 执行 SQL 字符串 | | `db.table(name)` | 获取表操作对象 | | `db.defineTable(name, cols)` | 定义表结构 | -| `db.transaction(fn)` | 执行事务 | +| `db.transaction(fn)` | 执行事务(自动回滚)🆕 | | `db.exportTable(name)` / `db.exportAll()` | 导出数据 JSON | | `db.importTable(name, data)` | 导入数据 | | `db.addMigration(v, fn)` / `db.migrateTo(v)` | 数据迁移 | | `db.subscribe(table, fn)` | 订阅表变更 | -| `db.on(hook, fn)` | 注册钩子 | +| `db.on(hook, fn)` | 注册钩子 (14 种) | + +### 连接池(v0.1.13) + +| 静态方法 | 说明 | +|------|------| +| `MetonaSqlark.connect(config)` | 获取或创建数据库实例(单例复用)🆕 | +| `MetonaSqlark.disconnect(name)` | 释放连接(引用计数 -1)🆕 | +| `MetonaSqlark.disconnectAll()` | 强制关闭所有连接 🆕 | +| `MetonaSqlark.getActiveConnections()` | 获取活跃连接列表 🆕 | ### React / Vue 集成 @@ -197,7 +235,7 @@ npm run typecheck # 类型检查 | 测试用例 | 264 | | 测试套件 | 15 | | 语句覆盖率 | 93.46% | -| SQL 关键字 | 29 | +| SQL 关键字 | 31 | | 存储引擎 | 4(Memory / IndexedDB / OPFS / Hybrid) | --- @@ -208,14 +246,16 @@ npm run typecheck # 类型检查 src/ ├── index.ts # 入口(MetonaSqlark + MeSqlark) ├── core.ts # 主类 -├── constants.ts # 类型定义 + 配置 +├── constants.ts # 类型定义 + 配置 + DatabaseError +├── connection-manager.ts # 连接池管理 🆕 +├── utils.ts # 工具函数 ├── engine/ # 存储引擎(Memory/IndexedDB/OPFS) -├── hybrid/ # 混合引擎 +├── hybrid/ # 混合引擎(write-through) ├── table/ # 表管理 + Schema 校验 ├── query/ # AST + Builder + Compiler + Executor -├── sql/ # Lexer + Parser -├── transaction/ # 事务管理 -├── plugin/ # 插件系统 +├── sql/ # Lexer + Parser(递归下降) +├── transaction/ # 事务管理(支持回滚) +├── plugin/ # 插件系统(14 hooks) └── integrations/ # React / Vue hooks ``` diff --git a/site/demo.html b/site/demo.html index 73de90c..45e69b8 100644 --- a/site/demo.html +++ b/site/demo.html @@ -82,7 +82,7 @@ 文档 演示 -

Memory 模式 — v0.1.12
+
Memory 模式 — v0.1.13
@@ -112,6 +112,7 @@ SELECT COUNT(*) as total_users, AVG(age) as avg_age FROM users; +
@@ -370,7 +371,21 @@ SELECT * FROM users WHERE id = '9'; DELETE FROM users WHERE id = '9'; -- 确认 -SELECT COUNT(*) as user_count FROM users;` +SELECT COUNT(*) as user_count FROM users;`, + subquery: `-- IN 子查询:有订单的用户 +SELECT name, email FROM users +WHERE id IN (SELECT user_id FROM orders); + +-- NOT IN 子查询:没有订单的用户 +SELECT name, email FROM users +WHERE id NOT IN (SELECT user_id FROM orders); + +-- 标量子查询:消费最高的用户 +SELECT name, age FROM users +WHERE id IN ( + SELECT user_id FROM orders + WHERE amount = (SELECT MAX(amount) FROM orders) +);` }; function loadPreset(name) { diff --git a/site/docs.html b/site/docs.html index e8598bb..c3d121d 100644 --- a/site/docs.html +++ b/site/docs.html @@ -85,7 +85,10 @@ GROUP BY & 聚合 WHERE 操作符

高级特性

- 事务 + 事务 & 回滚 + 子查询 + 外键级联 + 连接池 数据迁移 导入导出 插件 & 钩子 @@ -189,6 +192,8 @@ db.isReady(); // true maxLengthnumber字符串最大长度 min/maxnumber数值范围 referencesstring外键引用 'table.column' + onDelete'CASCADE'\|'SET NULL'\|'RESTRICT'删除级联 🆕 + onUpdate'CASCADE'\|'SET NULL'\|'RESTRICT'更新级联 🆕

🔍 SQL 查询

@@ -352,21 +357,83 @@ db.isReady(); // true name: { $like: 'A%', $ne: 'Admin' }, }) -

🔒 事务

-

保证原子性,事务内步骤失败自动报错。

+

🔒 事务 & 回滚

+

v0.1.13 起支持真正的自动回滚:事务内任何一步失败,所有变更自动撤销。

await db.transaction(async (trx) => {
   // trx.table() 获取事务内表操作对象
   await trx.table('users').insert({ id: '3', name: 'Charlie' });
   await trx.table('orders').insert({ id: 'o1', userId: '3', amount: 99 });
-  await trx.table('accounts').update({ balance: 1 })
-    .where({ userId: '3' })
-    .execute();
-
-  // 也可返回结果
-  return 'success';
+  // ✅ 全部成功 → 自动 commit
+  // ❌ 任何一步失败 → 自动 rollback,数据恢复原状
+  // Memory 引擎:快照回滚 | IndexedDB 引擎:延迟写入
 });
+

🔍 子查询

+

v0.1.13 新增子查询支持,可在 WHERE 条件中嵌套 SELECT。

+ +
// IN 子查询 — 查询有高额订单的用户
+await db.query(`SELECT * FROM users
+  WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`);
+
+// 标量子查询 — 查询年龄等于平均年龄的用户
+await db.query(`SELECT * FROM users
+  WHERE age = (SELECT AVG(age) FROM users)`);
+
+// NOT IN 子查询
+await db.query(`SELECT * FROM users
+  WHERE id NOT IN (SELECT user_id FROM orders)`);
+ +

🔗 外键级联

+

v0.1.13 支持外键级联操作,定义表时可指定 ON DELETE / ON UPDATE 行为。

+ +
// 定义时指定外键 + 级联策略
+await db.defineTable('orders', {
+  id: { type: 'string', primaryKey: true },
+  user_id: {
+    type: 'string',
+    references: 'users.id',
+    onDelete: 'CASCADE',  // 删除用户时级联删除订单
+    onUpdate: 'RESTRICT', // 禁止更新被引用的用户 ID
+  },
+  amount: { type: 'number' },
+});
+
+// SQL DDL 同样支持
+await db.query(`CREATE TABLE orders (
+  id STRING PRIMARY KEY,
+  user_id STRING REFERENCES users(id) ON DELETE CASCADE ON UPDATE RESTRICT,
+  amount NUMBER
+)`);
+
+// 删除用户 → 其所有订单自动删除
+await db.query("DELETE FROM users WHERE id = '1'");
+ + + + + + +
级联选项行为
CASCADE级联删除/更新子表中的匹配行
SET NULL将子表中的外键列设为 NULL
RESTRICT禁止操作(默认行为)
+ +

🏊 连接池

+

v0.1.13 新增连接池管理器,避免重复创建同名数据库实例。

+ +
// connect() — 获取或创建实例(单例复用)
+const db1 = await MetonaSqlark.connect({ name: 'my-app', mode: 'hybrid' });
+const db2 = await MetonaSqlark.connect({ name: 'my-app' });
+// db1 === db2 — 复用已有实例,避免重复 open IndexedDB
+
+// disconnect() — 释放连接(引用计数 -1)
+await db2.disconnect(); // 引用计数: 2 → 1
+await db1.disconnect(); // 引用计数: 1 → 0,自动 close()
+
+// disconnectAll() — 强制关闭所有连接
+await MetonaSqlark.disconnectAll();
+
+// getActiveConnections() — 查看活跃连接
+MetonaSqlark.getActiveConnections(); // ['my-app']
+

🔄 数据迁移

按版本号管理表结构变更。

diff --git a/site/index.html b/site/index.html index 87d5bab..8500724 100644 --- a/site/index.html +++ b/site/index.html @@ -151,7 +151,7 @@
-
v0.1.12 已发布
+
v0.1.13 已发布 — 事务回滚 · 子查询 · 外键级联 · 连接池

前端的 SQL 数据库

TypeScript 原生构建,内存与磁盘双模式,支持完整 SQL 查询。
零运行时依赖,开箱即用。

@@ -237,32 +237,42 @@ npm install @metona-team/metona-sqlark

完整 SQL 解析器

-

手写递归下降 SQL 解析器,无第三方依赖。支持 SELECT / INSERT / UPDATE / DELETE / CREATE TABLE / DROP TABLE / JOIN / GROUP BY / HAVING / DISTINCT。

-
-
-
🔗
-

Query Builder

-

链式 API + TypeScript 泛型支持。.select().where().innerJoin().orderBy().limit() — IDE 自动补全,类型安全。

+

手写递归下降 SQL 解析器。SELECT / INSERT / UPDATE / DELETE / JOIN / GROUP BY / HAVING / DISTINCT / 子查询。

🔒
-

事务支持

-

原子性操作,事务内任意步骤失败自动回滚。IndexedDB 引擎原生事务 + Memory 引擎模拟事务。

+

事务回滚 NEW

+

beginTransaction / commitTransaction / rollbackTransaction 三件套。失败自动回滚,Memory 快照 + IndexedDB 延迟写入。

+
+
+
🔍
+

子查询 NEW

+

IN (SELECT ...) + 标量子查询。递归执行,自动将子查询结果替换为具体值。

+
+
+
🔗
+

外键级联 NEW

+

references + ON DELETE CASCADE / SET NULL / RESTRICT。递归级联删除,自动维护引用完整性。

+
+
+
🏊
+

连接池 NEW

+

MetonaSqlark.connect() 单例复用,引用计数管理。避免重复打开 IndexedDB,自动释放资源。

🧩

插件系统

-

14 种生命周期钩子:beforeCreateTable / afterInsert / beforeQuery / afterTransaction……按优先级注册,完整生命周期管理。

+

14 种生命周期钩子:beforeCreateTable / afterInsert / beforeQuery / afterTransaction……按优先级注册。

📦

零运行时依赖

-

纯 TypeScript 实现,不依赖任何第三方库。Tree-shakable,UMD/ESM/CJS 多格式输出,最小体积 ~10KB gzip。

+

纯 TypeScript 实现,不依赖任何第三方库。Tree-shakable,UMD/ESM/CJS 多格式输出,~10KB gzip。

📊

聚合 & 分组

-

COUNT / SUM / AVG / MIN / MAX 五大聚合函数,GROUP BY + HAVING 子句,DISTINCT 去重,完整的数据分析能力。

+

COUNT / SUM / AVG / MIN / MAX 五大聚合函数,GROUP BY + HAVING 子句,DISTINCT 去重。

🔄
@@ -271,8 +281,13 @@ npm install @metona-team/metona-sqlark
📡
-

发布订阅

-

subscribe() 订阅表变更事件,emit() 触发通知。React useQuery / Vue useSqlarkQuery 开箱即用。

+

发布订阅 + 框架集成

+

subscribe() 订阅表变更。React useQuery / Vue useSqlarkQuery 开箱即用。

+
+
+
🔗
+

Query Builder

+

链式 API + TypeScript 泛型。.select().where().innerJoin().orderBy().limit() — 类型安全,IDE 友好。

@@ -312,13 +327,13 @@ npm install @metona-team/metona-sqlark

5 行代码开始

-

像写 SQL 一样操作前端数据 — 或使用 Query Builder 链式 API

+

像写 SQL 一样操作前端数据 — v0.1.13 新增子查询、外键级联、事务回滚

// 创建数据库 — MeSqlark 是别名,完全等价
 const db = await MetonaSqlark.create({ name: 'my-app', mode: 'hybrid' });
 
-// 定义表结构
+// 定义表结构 — 支持外键级联
 await db.defineTable('users', {
   id: { type: 'string', primaryKey: true },
   name: { type: 'string', required: true },
@@ -344,11 +359,22 @@ npm install @metona-team/metona-sqlark
 await db.query(`SELECT dept, COUNT(*) as cnt, AVG(salary) as avg
   FROM employees GROUP BY dept HAVING COUNT(*) > 1`);
 
-// 事务 — 原子操作
+// 事务 — 失败自动回滚
 await db.transaction(async (trx) => {
   await trx.table('users').insert({ id: '3', name: 'Charlie' });
   await trx.table('orders').insert({ id: 'o1', userId: '3', amount: 99 });
-});
+ // 任何一步失败 → 自动回滚 +}); + +// 子查询 — IN (SELECT ...) +await db.query(`SELECT * FROM users + WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`); + +// 连接池 — 单例复用,避免重复打开 IndexedDB +const db1 = await MetonaSqlark.connect({ name: 'my-app' }); +const db2 = await MetonaSqlark.connect({ name: 'my-app' }); +// db1 === db2(复用已有实例) +await db2.disconnect(); // 引用计数 -1,归零时自动 close