feat: metona-sqlark v0.1.12 — 前端TypeScript关系型数据库
- 4种存储引擎:Memory / IndexedDB / OPFS / Hybrid - 完整SQL支持:SELECT/INSERT/UPDATE/DELETE/JOIN/GROUP BY/HAVING/DISTINCT - Query Builder链式API + TypeScript泛型支持 - 聚合函数:COUNT/SUM/AVG/MIN/MAX - 事务、插件系统(14 hooks)、发布订阅、数据迁移、导入导出 - React/Vue框架集成 - 264个测试用例,93.46%覆盖率 - 零运行时依赖
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* metona-sqlark Hybrid Engine — 内存 + 磁盘混合存储引擎
|
||||
* @module hybrid/index
|
||||
*
|
||||
* 采用 write-through 策略:
|
||||
* - 所有写操作同时写入内存和磁盘
|
||||
* - 所有读操作直接从内存返回
|
||||
* - 数据库打开时从磁盘加载数据到内存
|
||||
*/
|
||||
|
||||
import type { IStorageEngine } from '../engine/interface';
|
||||
import type { QueryPlan, TableSchema, DiskEngine } from '../constants';
|
||||
import { MemoryEngine } from '../engine/memory';
|
||||
import { IndexedDBEngine } from '../engine/indexeddb';
|
||||
import { OPFSEngine } from '../engine/opfs';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HybridEngine
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export class HybridEngine implements IStorageEngine {
|
||||
readonly name = 'hybrid';
|
||||
|
||||
private memoryEngine: MemoryEngine;
|
||||
private diskEngine: IStorageEngine;
|
||||
private diskEngineType: DiskEngine;
|
||||
|
||||
constructor(diskEngine: DiskEngine = 'indexeddb') {
|
||||
this.memoryEngine = new MemoryEngine();
|
||||
this.diskEngineType = diskEngine;
|
||||
this.diskEngine = diskEngine === 'opfs' ? new OPFSEngine() : new IndexedDBEngine();
|
||||
}
|
||||
|
||||
// ---- 生命周期 ----
|
||||
|
||||
async open(dbName: string, version: number): Promise<void> {
|
||||
// 先打开磁盘引擎
|
||||
await this.diskEngine.open(dbName, version);
|
||||
|
||||
// 再打开内存引擎
|
||||
await this.memoryEngine.open(dbName, version);
|
||||
|
||||
// 从磁盘加载现存表
|
||||
const tableNames = await this.diskEngine.getTableNames();
|
||||
for (const tableName of tableNames) {
|
||||
const schema = await this.diskEngine.getTableSchema(tableName);
|
||||
if (!schema) continue;
|
||||
|
||||
// 在内存中创建表
|
||||
await this.memoryEngine.createTable(schema);
|
||||
|
||||
// 从磁盘加载数据到内存
|
||||
const rows = await this.diskEngine.find(tableName, { table: tableName });
|
||||
if (rows.length > 0) {
|
||||
try {
|
||||
await this.memoryEngine.insert(tableName, rows);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`[metona-sqlark] Failed to load table "${tableName}" data from disk:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this.memoryEngine.close();
|
||||
await this.diskEngine.close();
|
||||
}
|
||||
|
||||
isOpen(): boolean {
|
||||
return this.memoryEngine.isOpen() && this.diskEngine.isOpen();
|
||||
}
|
||||
|
||||
// ---- 表管理 ----
|
||||
|
||||
async createTable(schema: TableSchema): Promise<void> {
|
||||
await this.memoryEngine.createTable(schema);
|
||||
await this.diskEngine.createTable(schema);
|
||||
}
|
||||
|
||||
async dropTable(tableName: string): Promise<void> {
|
||||
await this.memoryEngine.dropTable(tableName);
|
||||
await this.diskEngine.dropTable(tableName);
|
||||
}
|
||||
|
||||
async hasTable(tableName: string): Promise<boolean> {
|
||||
return this.memoryEngine.hasTable(tableName);
|
||||
}
|
||||
|
||||
async getTableNames(): Promise<string[]> {
|
||||
return this.memoryEngine.getTableNames();
|
||||
}
|
||||
|
||||
async getTableSchema(tableName: string): Promise<TableSchema | null> {
|
||||
return this.memoryEngine.getTableSchema(tableName);
|
||||
}
|
||||
|
||||
// ---- CRUD(write-through 策略) ----
|
||||
|
||||
async insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]> {
|
||||
const pks = await this.memoryEngine.insert(tableName, rows);
|
||||
// write-through: 同步写入磁盘
|
||||
await this.diskEngine.insert(tableName, rows);
|
||||
return pks;
|
||||
}
|
||||
|
||||
async find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]> {
|
||||
// 直接从内存读取
|
||||
return this.memoryEngine.find(tableName, query);
|
||||
}
|
||||
|
||||
async update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number> {
|
||||
const count = await this.memoryEngine.update(tableName, query, updates);
|
||||
// write-through: 同步更新磁盘
|
||||
await this.diskEngine.update(tableName, query, updates);
|
||||
return count;
|
||||
}
|
||||
|
||||
async delete(tableName: string, query: QueryPlan): Promise<number> {
|
||||
const count = await this.memoryEngine.delete(tableName, query);
|
||||
// write-through: 同步删除磁盘
|
||||
await this.diskEngine.delete(tableName, query);
|
||||
return count;
|
||||
}
|
||||
|
||||
async count(tableName: string, query?: QueryPlan): Promise<number> {
|
||||
return this.memoryEngine.count(tableName, query);
|
||||
}
|
||||
|
||||
async clear(tableName: string): Promise<void> {
|
||||
await this.memoryEngine.clear(tableName);
|
||||
await this.diskEngine.clear(tableName);
|
||||
}
|
||||
|
||||
// ---- 引擎信息 ----
|
||||
|
||||
/** 获取磁盘引擎类型 */
|
||||
getDiskEngineType(): DiskEngine {
|
||||
return this.diskEngineType;
|
||||
}
|
||||
|
||||
/** 获取内存引擎(供内部使用) */
|
||||
getMemoryEngine(): MemoryEngine {
|
||||
return this.memoryEngine;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user