docs: README + site 更新 v0.1.13 新特性(事务回滚/子查询/外键级联/连接池)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# MetonaSqlark
|
# MetonaSqlark
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://img.shields.io/badge/version-0.1.12-blue?style=flat-square" alt="version">
|
<img src="https://img.shields.io/badge/version-0.1.13-blue?style=flat-square" alt="version">
|
||||||
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license">
|
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license">
|
||||||
<img src="https://img.shields.io/badge/coverage-93.46%25-brightgreen?style=flat-square" alt="coverage">
|
<img src="https://img.shields.io/badge/coverage-93.46%25-brightgreen?style=flat-square" alt="coverage">
|
||||||
<img src="https://img.shields.io/badge/tests-264%20passed-success?style=flat-square" alt="tests">
|
<img src="https://img.shields.io/badge/tests-264%20passed-success?style=flat-square" alt="tests">
|
||||||
@@ -11,6 +11,15 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## ✨ v0.1.13 新特性
|
||||||
|
|
||||||
|
- 🔒 **事务回滚** — `beginTransaction/commitTransaction/rollbackTransaction` 真正的原子操作
|
||||||
|
- 🔍 **子查询** — `IN (SELECT ...)` 和标量子查询 `= (SELECT ...)`
|
||||||
|
- 🔗 **外键级联** — `ON DELETE CASCADE / SET NULL / RESTRICT` 递归级联删除
|
||||||
|
- 🏊 **连接池** — `MetonaSqlark.connect()` 单例复用,引用计数管理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📦 安装
|
## 📦 安装
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -72,15 +81,6 @@ const { MetonaSqlark } = require('@metona-team/metona-sqlark');
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### ES Module in Browser
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script type="module">
|
|
||||||
import { MetonaSqlark } from './metona-sqlark.esm.js';
|
|
||||||
const db = await MetonaSqlark.create({ name: 'my-app', mode: 'hybrid' });
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 🚀 快速开始
|
||||||
@@ -94,7 +94,7 @@ const db = await MetonaSqlark.create({
|
|||||||
diskEngine: 'indexeddb', // 'indexeddb' | 'opfs'
|
diskEngine: 'indexeddb', // 'indexeddb' | 'opfs'
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义表
|
// 定义表 — 支持外键级联
|
||||||
await db.defineTable('users', {
|
await db.defineTable('users', {
|
||||||
id: { type: 'string', primaryKey: true },
|
id: { type: 'string', primaryKey: true },
|
||||||
name: { type: 'string', required: true },
|
name: { type: 'string', required: true },
|
||||||
@@ -102,6 +102,12 @@ await db.defineTable('users', {
|
|||||||
email: { type: 'string', unique: true, index: true },
|
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 查询
|
// SQL 查询
|
||||||
await db.query("INSERT INTO users VALUES ('1', 'Alice', 30, 'alice@demo.com')");
|
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');
|
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
|
// JOIN
|
||||||
await db.query(`SELECT u.name, o.product FROM users u INNER JOIN orders o ON u.id = o.user_id`);
|
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
|
// GROUP BY
|
||||||
await db.query(`SELECT dept, COUNT(*) FROM employees GROUP BY dept HAVING COUNT(*) > 1`);
|
await db.query(`SELECT dept, COUNT(*) FROM employees GROUP BY dept HAVING COUNT(*) > 1`);
|
||||||
|
|
||||||
// 事务
|
// 事务 — 自动回滚 v0.1.13
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
await trx.table('users').insert({ id: '3', name: 'Charlie' });
|
await trx.table('users').insert({ id: '3', name: 'Charlie' });
|
||||||
await trx.table('orders').insert({ id: 'o1', userId: '3', amount: 99 });
|
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'` | 磁盘引擎 |
|
| `diskEngine` | `'indexeddb' \| 'opfs'` | `'indexeddb'` | 磁盘引擎 |
|
||||||
| `version` | `number` | `1` | 版本号 |
|
| `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 操作符
|
### WHERE 操作符
|
||||||
|
|
||||||
| 操作符 | 含义 | 操作符 | 含义 |
|
| 操作符 | 含义 | 操作符 | 含义 |
|
||||||
@@ -156,12 +185,21 @@ await db.transaction(async (trx) => {
|
|||||||
| `db.query(sql)` | 执行 SQL 字符串 |
|
| `db.query(sql)` | 执行 SQL 字符串 |
|
||||||
| `db.table(name)` | 获取表操作对象 |
|
| `db.table(name)` | 获取表操作对象 |
|
||||||
| `db.defineTable(name, cols)` | 定义表结构 |
|
| `db.defineTable(name, cols)` | 定义表结构 |
|
||||||
| `db.transaction(fn)` | 执行事务 |
|
| `db.transaction(fn)` | 执行事务(自动回滚)🆕 |
|
||||||
| `db.exportTable(name)` / `db.exportAll()` | 导出数据 JSON |
|
| `db.exportTable(name)` / `db.exportAll()` | 导出数据 JSON |
|
||||||
| `db.importTable(name, data)` | 导入数据 |
|
| `db.importTable(name, data)` | 导入数据 |
|
||||||
| `db.addMigration(v, fn)` / `db.migrateTo(v)` | 数据迁移 |
|
| `db.addMigration(v, fn)` / `db.migrateTo(v)` | 数据迁移 |
|
||||||
| `db.subscribe(table, fn)` | 订阅表变更 |
|
| `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 集成
|
### React / Vue 集成
|
||||||
|
|
||||||
@@ -197,7 +235,7 @@ npm run typecheck # 类型检查
|
|||||||
| 测试用例 | 264 |
|
| 测试用例 | 264 |
|
||||||
| 测试套件 | 15 |
|
| 测试套件 | 15 |
|
||||||
| 语句覆盖率 | 93.46% |
|
| 语句覆盖率 | 93.46% |
|
||||||
| SQL 关键字 | 29 |
|
| SQL 关键字 | 31 |
|
||||||
| 存储引擎 | 4(Memory / IndexedDB / OPFS / Hybrid) |
|
| 存储引擎 | 4(Memory / IndexedDB / OPFS / Hybrid) |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -208,14 +246,16 @@ npm run typecheck # 类型检查
|
|||||||
src/
|
src/
|
||||||
├── index.ts # 入口(MetonaSqlark + MeSqlark)
|
├── index.ts # 入口(MetonaSqlark + MeSqlark)
|
||||||
├── core.ts # 主类
|
├── core.ts # 主类
|
||||||
├── constants.ts # 类型定义 + 配置
|
├── constants.ts # 类型定义 + 配置 + DatabaseError
|
||||||
|
├── connection-manager.ts # 连接池管理 🆕
|
||||||
|
├── utils.ts # 工具函数
|
||||||
├── engine/ # 存储引擎(Memory/IndexedDB/OPFS)
|
├── engine/ # 存储引擎(Memory/IndexedDB/OPFS)
|
||||||
├── hybrid/ # 混合引擎
|
├── hybrid/ # 混合引擎(write-through)
|
||||||
├── table/ # 表管理 + Schema 校验
|
├── table/ # 表管理 + Schema 校验
|
||||||
├── query/ # AST + Builder + Compiler + Executor
|
├── query/ # AST + Builder + Compiler + Executor
|
||||||
├── sql/ # Lexer + Parser
|
├── sql/ # Lexer + Parser(递归下降)
|
||||||
├── transaction/ # 事务管理
|
├── transaction/ # 事务管理(支持回滚)
|
||||||
├── plugin/ # 插件系统
|
├── plugin/ # 插件系统(14 hooks)
|
||||||
└── integrations/ # React / Vue hooks
|
└── integrations/ # React / Vue hooks
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+17
-2
@@ -82,7 +82,7 @@
|
|||||||
<a href="docs.html">文档</a>
|
<a href="docs.html">文档</a>
|
||||||
<a href="demo.html" class="nav-active">演示</a>
|
<a href="demo.html" class="nav-active">演示</a>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="status"><span class="dot"></span> Memory 模式 — v0.1.12</div>
|
<div class="status"><span class="dot"></span> Memory 模式 — v0.1.13</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="main">
|
<div class="main">
|
||||||
@@ -112,6 +112,7 @@ SELECT COUNT(*) as total_users, AVG(age) as avg_age FROM users;</textarea>
|
|||||||
<button class="btn btn-preset" onclick="loadPreset('agg')">📊 聚合</button>
|
<button class="btn btn-preset" onclick="loadPreset('agg')">📊 聚合</button>
|
||||||
<button class="btn btn-preset" onclick="loadPreset('ddl')">🏗 DDL</button>
|
<button class="btn btn-preset" onclick="loadPreset('ddl')">🏗 DDL</button>
|
||||||
<button class="btn btn-preset" onclick="loadPreset('crud')">✏️ CRUD</button>
|
<button class="btn btn-preset" onclick="loadPreset('crud')">✏️ CRUD</button>
|
||||||
|
<button class="btn btn-preset" onclick="loadPreset('subquery')">🔍 子查询</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -370,7 +371,21 @@ SELECT * FROM users WHERE id = '9';
|
|||||||
DELETE 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) {
|
function loadPreset(name) {
|
||||||
|
|||||||
+76
-9
@@ -85,7 +85,10 @@
|
|||||||
<a href="#groupby">GROUP BY & 聚合</a>
|
<a href="#groupby">GROUP BY & 聚合</a>
|
||||||
<a href="#where-ops">WHERE 操作符</a>
|
<a href="#where-ops">WHERE 操作符</a>
|
||||||
<h4>高级特性</h4>
|
<h4>高级特性</h4>
|
||||||
<a href="#transaction">事务</a>
|
<a href="#transaction">事务 & 回滚</a>
|
||||||
|
<a href="#subquery">子查询</a>
|
||||||
|
<a href="#foreign-key">外键级联</a>
|
||||||
|
<a href="#connection-pool">连接池</a>
|
||||||
<a href="#migration">数据迁移</a>
|
<a href="#migration">数据迁移</a>
|
||||||
<a href="#export">导入导出</a>
|
<a href="#export">导入导出</a>
|
||||||
<a href="#plugin">插件 & 钩子</a>
|
<a href="#plugin">插件 & 钩子</a>
|
||||||
@@ -189,6 +192,8 @@ db.<span class="f">isReady</span>(); <span class="c">// true</span>
|
|||||||
<tr><td><code>maxLength</code></td><td><code>number</code></td><td>字符串最大长度</td></tr>
|
<tr><td><code>maxLength</code></td><td><code>number</code></td><td>字符串最大长度</td></tr>
|
||||||
<tr><td><code>min</code>/<code>max</code></td><td><code>number</code></td><td>数值范围</td></tr>
|
<tr><td><code>min</code>/<code>max</code></td><td><code>number</code></td><td>数值范围</td></tr>
|
||||||
<tr><td><code>references</code></td><td><code>string</code></td><td>外键引用 <code>'table.column'</code></td></tr>
|
<tr><td><code>references</code></td><td><code>string</code></td><td>外键引用 <code>'table.column'</code></td></tr>
|
||||||
|
<tr><td><code>onDelete</code></td><td><code>'CASCADE'\|'SET NULL'\|'RESTRICT'</code></td><td>删除级联 🆕</td></tr>
|
||||||
|
<tr><td><code>onUpdate</code></td><td><code>'CASCADE'\|'SET NULL'\|'RESTRICT'</code></td><td>更新级联 🆕</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2 id="sql-query">🔍 SQL 查询</h2>
|
<h2 id="sql-query">🔍 SQL 查询</h2>
|
||||||
@@ -352,21 +357,83 @@ db.<span class="f">isReady</span>(); <span class="c">// true</span>
|
|||||||
<span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span>, <span class="s">$ne</span>: <span class="s">'Admin'</span> },
|
<span class="s">name</span>: { <span class="s">$like</span>: <span class="s">'A%'</span>, <span class="s">$ne</span>: <span class="s">'Admin'</span> },
|
||||||
})</pre>
|
})</pre>
|
||||||
|
|
||||||
<h2 id="transaction">🔒 事务</h2>
|
<h2 id="transaction">🔒 事务 & 回滚</h2>
|
||||||
<p>保证原子性,事务内步骤失败自动报错。</p>
|
<p>v0.1.13 起支持真正的自动回滚:事务内任何一步失败,所有变更自动撤销。</p>
|
||||||
|
|
||||||
<pre><span class="k">await</span> db.<span class="f">transaction</span>(<span class="k">async</span> (trx) => {
|
<pre><span class="k">await</span> db.<span class="f">transaction</span>(<span class="k">async</span> (trx) => {
|
||||||
<span class="c">// trx.table() 获取事务内表操作对象</span>
|
<span class="c">// trx.table() 获取事务内表操作对象</span>
|
||||||
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'3'</span>, <span class="s">name</span>: <span class="s">'Charlie'</span> });
|
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'users'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'3'</span>, <span class="s">name</span>: <span class="s">'Charlie'</span> });
|
||||||
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'orders'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'o1'</span>, <span class="s">userId</span>: <span class="s">'3'</span>, <span class="s">amount</span>: <span class="n">99</span> });
|
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'orders'</span>).<span class="f">insert</span>({ <span class="s">id</span>: <span class="s">'o1'</span>, <span class="s">userId</span>: <span class="s">'3'</span>, <span class="s">amount</span>: <span class="n">99</span> });
|
||||||
<span class="k">await</span> trx.<span class="f">table</span>(<span class="s">'accounts'</span>).<span class="f">update</span>({ <span class="s">balance</span>: <span class="n">1</span> })
|
<span class="c">// ✅ 全部成功 → 自动 commit</span>
|
||||||
.<span class="f">where</span>({ <span class="s">userId</span>: <span class="s">'3'</span> })
|
<span class="c">// ❌ 任何一步失败 → 自动 rollback,数据恢复原状</span>
|
||||||
.<span class="f">execute</span>();
|
<span class="c">// Memory 引擎:快照回滚 | IndexedDB 引擎:延迟写入</span>
|
||||||
|
|
||||||
<span class="c">// 也可返回结果</span>
|
|
||||||
<span class="k">return</span> <span class="s">'success'</span>;
|
|
||||||
});</pre>
|
});</pre>
|
||||||
|
|
||||||
|
<h2 id="subquery">🔍 子查询</h2>
|
||||||
|
<p>v0.1.13 新增子查询支持,可在 WHERE 条件中嵌套 SELECT。</p>
|
||||||
|
|
||||||
|
<pre><span class="c">// IN 子查询 — 查询有高额订单的用户</span>
|
||||||
|
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
|
||||||
|
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`</span>);
|
||||||
|
|
||||||
|
<span class="c">// 标量子查询 — 查询年龄等于平均年龄的用户</span>
|
||||||
|
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
|
||||||
|
WHERE age = (SELECT AVG(age) FROM users)`</span>);
|
||||||
|
|
||||||
|
<span class="c">// NOT IN 子查询</span>
|
||||||
|
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`SELECT * FROM users
|
||||||
|
WHERE id NOT IN (SELECT user_id FROM orders)`</span>);</pre>
|
||||||
|
|
||||||
|
<h2 id="foreign-key">🔗 外键级联</h2>
|
||||||
|
<p>v0.1.13 支持外键级联操作,定义表时可指定 ON DELETE / ON UPDATE 行为。</p>
|
||||||
|
|
||||||
|
<pre><span class="c">// 定义时指定外键 + 级联策略</span>
|
||||||
|
<span class="k">await</span> db.<span class="f">defineTable</span>(<span class="s">'orders'</span>, {
|
||||||
|
<span class="s">id</span>: { <span class="s">type</span>: <span class="s">'string'</span>, <span class="s">primaryKey</span>: <span class="k">true</span> },
|
||||||
|
<span class="s">user_id</span>: {
|
||||||
|
<span class="s">type</span>: <span class="s">'string'</span>,
|
||||||
|
<span class="s">references</span>: <span class="s">'users.id'</span>,
|
||||||
|
<span class="s">onDelete</span>: <span class="s">'CASCADE'</span>, <span class="c">// 删除用户时级联删除订单</span>
|
||||||
|
<span class="s">onUpdate</span>: <span class="s">'RESTRICT'</span>, <span class="c">// 禁止更新被引用的用户 ID</span>
|
||||||
|
},
|
||||||
|
<span class="s">amount</span>: { <span class="s">type</span>: <span class="s">'number'</span> },
|
||||||
|
});
|
||||||
|
|
||||||
|
<span class="c">// SQL DDL 同样支持</span>
|
||||||
|
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">`CREATE TABLE orders (
|
||||||
|
id STRING PRIMARY KEY,
|
||||||
|
user_id STRING REFERENCES users(id) ON DELETE CASCADE ON UPDATE RESTRICT,
|
||||||
|
amount NUMBER
|
||||||
|
)`</span>);
|
||||||
|
|
||||||
|
<span class="c">// 删除用户 → 其所有订单自动删除</span>
|
||||||
|
<span class="k">await</span> db.<span class="f">query</span>(<span class="s">"DELETE FROM users WHERE id = '1'"</span>);</pre>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr><th>级联选项</th><th>行为</th></tr>
|
||||||
|
<tr><td><code>CASCADE</code></td><td>级联删除/更新子表中的匹配行</td></tr>
|
||||||
|
<tr><td><code>SET NULL</code></td><td>将子表中的外键列设为 NULL</td></tr>
|
||||||
|
<tr><td><code>RESTRICT</code></td><td>禁止操作(默认行为)</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2 id="connection-pool">🏊 连接池</h2>
|
||||||
|
<p>v0.1.13 新增连接池管理器,避免重复创建同名数据库实例。</p>
|
||||||
|
|
||||||
|
<pre><span class="c">// connect() — 获取或创建实例(单例复用)</span>
|
||||||
|
<span class="k">const</span> db1 = <span class="k">await</span> <span class="f">MetonaSqlark.connect</span>({ <span class="s">name</span>: <span class="s">'my-app'</span>, <span class="s">mode</span>: <span class="s">'hybrid'</span> });
|
||||||
|
<span class="k">const</span> db2 = <span class="k">await</span> <span class="f">MetonaSqlark.connect</span>({ <span class="s">name</span>: <span class="s">'my-app'</span> });
|
||||||
|
<span class="c">// db1 === db2 — 复用已有实例,避免重复 open IndexedDB</span>
|
||||||
|
|
||||||
|
<span class="c">// disconnect() — 释放连接(引用计数 -1)</span>
|
||||||
|
<span class="k">await</span> db2.<span class="f">disconnect</span>(); <span class="c">// 引用计数: 2 → 1</span>
|
||||||
|
<span class="k">await</span> db1.<span class="f">disconnect</span>(); <span class="c">// 引用计数: 1 → 0,自动 close()</span>
|
||||||
|
|
||||||
|
<span class="c">// disconnectAll() — 强制关闭所有连接</span>
|
||||||
|
<span class="k">await</span> <span class="f">MetonaSqlark.disconnectAll</span>();
|
||||||
|
|
||||||
|
<span class="c">// getActiveConnections() — 查看活跃连接</span>
|
||||||
|
<span class="f">MetonaSqlark.getActiveConnections</span>(); <span class="c">// ['my-app']</span></pre>
|
||||||
|
|
||||||
<h2 id="migration">🔄 数据迁移</h2>
|
<h2 id="migration">🔄 数据迁移</h2>
|
||||||
<p>按版本号管理表结构变更。</p>
|
<p>按版本号管理表结构变更。</p>
|
||||||
|
|
||||||
|
|||||||
+44
-18
@@ -151,7 +151,7 @@
|
|||||||
<!-- Hero -->
|
<!-- Hero -->
|
||||||
<section class="hero">
|
<section class="hero">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="badge" style="margin-bottom:24px;"><span class="dot"></span> v0.1.12 已发布</div>
|
<div class="badge" style="margin-bottom:24px;"><span class="dot"></span> v0.1.13 已发布 — 事务回滚 · 子查询 · 外键级联 · 连接池</div>
|
||||||
<h1>前端的 <span class="gradient-text">SQL 数据库</span></h1>
|
<h1>前端的 <span class="gradient-text">SQL 数据库</span></h1>
|
||||||
<p>TypeScript 原生构建,内存与磁盘双模式,支持完整 SQL 查询。<br>零运行时依赖,开箱即用。</p>
|
<p>TypeScript 原生构建,内存与磁盘双模式,支持完整 SQL 查询。<br>零运行时依赖,开箱即用。</p>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
@@ -237,32 +237,42 @@ npm install @metona-team/metona-sqlark
|
|||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<div class="icon">⚡</div>
|
<div class="icon">⚡</div>
|
||||||
<h3>完整 SQL 解析器</h3>
|
<h3>完整 SQL 解析器</h3>
|
||||||
<p>手写递归下降 SQL 解析器,无第三方依赖。支持 SELECT / INSERT / UPDATE / DELETE / CREATE TABLE / DROP TABLE / JOIN / GROUP BY / HAVING / DISTINCT。</p>
|
<p>手写递归下降 SQL 解析器。SELECT / INSERT / UPDATE / DELETE / JOIN / GROUP BY / HAVING / DISTINCT / 子查询。</p>
|
||||||
</div>
|
|
||||||
<div class="feature-card">
|
|
||||||
<div class="icon">🔗</div>
|
|
||||||
<h3>Query Builder</h3>
|
|
||||||
<p>链式 API + TypeScript 泛型支持。.select().where().innerJoin().orderBy().limit() — IDE 自动补全,类型安全。</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<div class="icon">🔒</div>
|
<div class="icon">🔒</div>
|
||||||
<h3>事务支持</h3>
|
<h3>事务回滚 <span style="font-size:0.65rem;color:var(--accent);vertical-align:super;">NEW</span></h3>
|
||||||
<p>原子性操作,事务内任意步骤失败自动回滚。IndexedDB 引擎原生事务 + Memory 引擎模拟事务。</p>
|
<p>beginTransaction / commitTransaction / rollbackTransaction 三件套。失败自动回滚,Memory 快照 + IndexedDB 延迟写入。</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="icon">🔍</div>
|
||||||
|
<h3>子查询 <span style="font-size:0.65rem;color:var(--accent);vertical-align:super;">NEW</span></h3>
|
||||||
|
<p>IN (SELECT ...) + 标量子查询。递归执行,自动将子查询结果替换为具体值。</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="icon">🔗</div>
|
||||||
|
<h3>外键级联 <span style="font-size:0.65rem;color:var(--accent);vertical-align:super;">NEW</span></h3>
|
||||||
|
<p>references + ON DELETE CASCADE / SET NULL / RESTRICT。递归级联删除,自动维护引用完整性。</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="icon">🏊</div>
|
||||||
|
<h3>连接池 <span style="font-size:0.65rem;color:var(--accent);vertical-align:super;">NEW</span></h3>
|
||||||
|
<p>MetonaSqlark.connect() 单例复用,引用计数管理。避免重复打开 IndexedDB,自动释放资源。</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<div class="icon">🧩</div>
|
<div class="icon">🧩</div>
|
||||||
<h3>插件系统</h3>
|
<h3>插件系统</h3>
|
||||||
<p>14 种生命周期钩子:beforeCreateTable / afterInsert / beforeQuery / afterTransaction……按优先级注册,完整生命周期管理。</p>
|
<p>14 种生命周期钩子:beforeCreateTable / afterInsert / beforeQuery / afterTransaction……按优先级注册。</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<div class="icon">📦</div>
|
<div class="icon">📦</div>
|
||||||
<h3>零运行时依赖</h3>
|
<h3>零运行时依赖</h3>
|
||||||
<p>纯 TypeScript 实现,不依赖任何第三方库。Tree-shakable,UMD/ESM/CJS 多格式输出,最小体积 ~10KB gzip。</p>
|
<p>纯 TypeScript 实现,不依赖任何第三方库。Tree-shakable,UMD/ESM/CJS 多格式输出,~10KB gzip。</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<div class="icon">📊</div>
|
<div class="icon">📊</div>
|
||||||
<h3>聚合 & 分组</h3>
|
<h3>聚合 & 分组</h3>
|
||||||
<p>COUNT / SUM / AVG / MIN / MAX 五大聚合函数,GROUP BY + HAVING 子句,DISTINCT 去重,完整的数据分析能力。</p>
|
<p>COUNT / SUM / AVG / MIN / MAX 五大聚合函数,GROUP BY + HAVING 子句,DISTINCT 去重。</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<div class="icon">🔄</div>
|
<div class="icon">🔄</div>
|
||||||
@@ -271,8 +281,13 @@ npm install @metona-team/metona-sqlark
|
|||||||
</div>
|
</div>
|
||||||
<div class="feature-card">
|
<div class="feature-card">
|
||||||
<div class="icon">📡</div>
|
<div class="icon">📡</div>
|
||||||
<h3>发布订阅</h3>
|
<h3>发布订阅 + 框架集成</h3>
|
||||||
<p>subscribe() 订阅表变更事件,emit() 触发通知。React useQuery / Vue useSqlarkQuery 开箱即用。</p>
|
<p>subscribe() 订阅表变更。React useQuery / Vue useSqlarkQuery 开箱即用。</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="icon">🔗</div>
|
||||||
|
<h3>Query Builder</h3>
|
||||||
|
<p>链式 API + TypeScript 泛型。.select().where().innerJoin().orderBy().limit() — 类型安全,IDE 友好。</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -312,13 +327,13 @@ npm install @metona-team/metona-sqlark
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h2>5 行<span>代码</span>开始</h2>
|
<h2>5 行<span>代码</span>开始</h2>
|
||||||
<p>像写 SQL 一样操作前端数据 — 或使用 Query Builder 链式 API</p>
|
<p>像写 SQL 一样操作前端数据 — v0.1.13 新增子查询、外键级联、事务回滚</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="code-block">
|
<div class="code-block">
|
||||||
<pre><span class="comment">// 创建数据库 — MeSqlark 是别名,完全等价</span>
|
<pre><span class="comment">// 创建数据库 — MeSqlark 是别名,完全等价</span>
|
||||||
<span class="keyword">const</span> db = <span class="keyword">await</span> <span class="func">MetonaSqlark.create</span>({ <span class="string">name</span>: <span class="string">'my-app'</span>, <span class="string">mode</span>: <span class="string">'hybrid'</span> });
|
<span class="keyword">const</span> db = <span class="keyword">await</span> <span class="func">MetonaSqlark.create</span>({ <span class="string">name</span>: <span class="string">'my-app'</span>, <span class="string">mode</span>: <span class="string">'hybrid'</span> });
|
||||||
|
|
||||||
<span class="comment">// 定义表结构</span>
|
<span class="comment">// 定义表结构 — 支持外键级联</span>
|
||||||
<span class="keyword">await</span> db.<span class="func">defineTable</span>(<span class="string">'users'</span>, {
|
<span class="keyword">await</span> db.<span class="func">defineTable</span>(<span class="string">'users'</span>, {
|
||||||
<span class="string">id</span>: { <span class="string">type</span>: <span class="string">'string'</span>, <span class="string">primaryKey</span>: <span class="keyword">true</span> },
|
<span class="string">id</span>: { <span class="string">type</span>: <span class="string">'string'</span>, <span class="string">primaryKey</span>: <span class="keyword">true</span> },
|
||||||
<span class="string">name</span>: { <span class="string">type</span>: <span class="string">'string'</span>, <span class="string">required</span>: <span class="keyword">true</span> },
|
<span class="string">name</span>: { <span class="string">type</span>: <span class="string">'string'</span>, <span class="string">required</span>: <span class="keyword">true</span> },
|
||||||
@@ -344,11 +359,22 @@ npm install @metona-team/metona-sqlark
|
|||||||
<span class="keyword">await</span> db.<span class="func">query</span>(<span class="string">`SELECT dept, COUNT(*) as cnt, AVG(salary) as avg
|
<span class="keyword">await</span> db.<span class="func">query</span>(<span class="string">`SELECT dept, COUNT(*) as cnt, AVG(salary) as avg
|
||||||
FROM employees GROUP BY dept HAVING COUNT(*) > 1`</span>);
|
FROM employees GROUP BY dept HAVING COUNT(*) > 1`</span>);
|
||||||
|
|
||||||
<span class="comment">// 事务 — 原子操作</span>
|
<span class="comment">// 事务 — 失败自动回滚</span>
|
||||||
<span class="keyword">await</span> db.<span class="func">transaction</span>(<span class="keyword">async</span> (trx) => {
|
<span class="keyword">await</span> db.<span class="func">transaction</span>(<span class="keyword">async</span> (trx) => {
|
||||||
<span class="keyword">await</span> trx.<span class="func">table</span>(<span class="string">'users'</span>).<span class="func">insert</span>({ id: <span class="string">'3'</span>, name: <span class="string">'Charlie'</span> });
|
<span class="keyword">await</span> trx.<span class="func">table</span>(<span class="string">'users'</span>).<span class="func">insert</span>({ id: <span class="string">'3'</span>, name: <span class="string">'Charlie'</span> });
|
||||||
<span class="keyword">await</span> trx.<span class="func">table</span>(<span class="string">'orders'</span>).<span class="func">insert</span>({ id: <span class="string">'o1'</span>, userId: <span class="string">'3'</span>, amount: <span class="number">99</span> });
|
<span class="keyword">await</span> trx.<span class="func">table</span>(<span class="string">'orders'</span>).<span class="func">insert</span>({ id: <span class="string">'o1'</span>, userId: <span class="string">'3'</span>, amount: <span class="number">99</span> });
|
||||||
});</pre>
|
<span class="comment">// 任何一步失败 → 自动回滚</span>
|
||||||
|
});
|
||||||
|
|
||||||
|
<span class="comment">// 子查询 — IN (SELECT ...)</span>
|
||||||
|
<span class="keyword">await</span> db.<span class="func">query</span>(<span class="string">`SELECT * FROM users
|
||||||
|
WHERE id IN (SELECT user_id FROM orders WHERE amount > 100)`</span>);
|
||||||
|
|
||||||
|
<span class="comment">// 连接池 — 单例复用,避免重复打开 IndexedDB</span>
|
||||||
|
<span class="keyword">const</span> db1 = <span class="keyword">await</span> <span class="func">MetonaSqlark.connect</span>({ name: <span class="string">'my-app'</span> });
|
||||||
|
<span class="keyword">const</span> db2 = <span class="keyword">await</span> <span class="func">MetonaSqlark.connect</span>({ name: <span class="string">'my-app'</span> });
|
||||||
|
<span class="comment">// db1 === db2(复用已有实例)</span>
|
||||||
|
<span class="keyword">await</span> db2.<span class="func">disconnect</span>(); <span class="comment">// 引用计数 -1,归零时自动 close</span></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user