diff --git a/README.md b/README.md index bda6d22..d5b7878 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MetonaSqlark
-
+
@@ -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 @@
文档
演示
-
maxLengthnumbermin/maxnumberreferencesstring'table.column'onDelete'CASCADE'\|'SET NULL'\|'RESTRICT'onUpdate'CASCADE'\|'SET NULL'\|'RESTRICT'保证原子性,事务内步骤失败自动报错。
+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 @@TypeScript 原生构建,内存与磁盘双模式,支持完整 SQL 查询。
零运行时依赖,开箱即用。
手写递归下降 SQL 解析器,无第三方依赖。支持 SELECT / INSERT / UPDATE / DELETE / CREATE TABLE / DROP TABLE / JOIN / GROUP BY / HAVING / DISTINCT。
-链式 API + TypeScript 泛型支持。.select().where().innerJoin().orderBy().limit() — IDE 自动补全,类型安全。
+手写递归下降 SQL 解析器。SELECT / INSERT / UPDATE / DELETE / JOIN / GROUP BY / HAVING / DISTINCT / 子查询。
原子性操作,事务内任意步骤失败自动回滚。IndexedDB 引擎原生事务 + Memory 引擎模拟事务。
+beginTransaction / commitTransaction / rollbackTransaction 三件套。失败自动回滚,Memory 快照 + IndexedDB 延迟写入。
+IN (SELECT ...) + 标量子查询。递归执行,自动将子查询结果替换为具体值。
+references + ON DELETE CASCADE / SET NULL / RESTRICT。递归级联删除,自动维护引用完整性。
+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 去重。
subscribe() 订阅表变更事件,emit() 触发通知。React useQuery / Vue useSqlarkQuery 开箱即用。
+subscribe() 订阅表变更。React useQuery / Vue useSqlarkQuery 开箱即用。
+链式 API + TypeScript 泛型。.select().where().innerJoin().orderBy().limit() — 类型安全,IDE 友好。
像写 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