(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MetonaSqlark = {})); })(this, (function (exports) { 'use strict'; /** * metona-sqlark Constants — 类型定义 / 默认配置 / 枚举 * @module constants */ /** 所有存储模式 */ /** 所有字段类型 */ const FIELD_TYPES = ['string', 'number', 'boolean', 'date', 'json']; /** 数据库默认配置 */ const DB_DEFAULTS = Object.freeze({ name: 'metona-sqlark', mode: 'hybrid', diskEngine: 'indexeddb', version: 1, }); // --------------------------------------------------------------------------- // 错误类型 // --------------------------------------------------------------------------- /** 数据库错误 */ class DatabaseError extends Error { constructor(message, code, details) { super(message); this.code = code; this.details = details; this.name = 'DatabaseError'; } } // --------------------------------------------------------------------------- // 版本 // --------------------------------------------------------------------------- const VERSION = '0.1.12'; /** * metona-sqlark Shared WHERE Matcher — 统一的条件匹配逻辑 * @module query/where-matcher * * MemoryEngine / IndexedDBEngine / QueryExecutor 共享此模块, * 消除 220+ 行重复代码,统一 $and/$or/$not/$col 行为。 */ // --------------------------------------------------------------------------- // LIKE 正则缓存 // --------------------------------------------------------------------------- const likeCache = new Map(); function compileLikeRegex(pattern) { const cached = likeCache.get(pattern); if (cached) return cached; const escaped = pattern .replace(/[.+^${}()|[\]\\]/g, '\\$&') .replace(/%/g, '.*') .replace(/_/g, '.'); const regex = new RegExp(`^${escaped}$`, 'i'); likeCache.set(pattern, regex); return regex; } // --------------------------------------------------------------------------- // WHERE 匹配(顶层入口) // --------------------------------------------------------------------------- /** * 匹配完整 WHERE 条件 * @param row 当前数据行 * @param where WHERE 条件对象 * @param options.$col 是否启用 $col 列引用解析 */ function matchWhere(row, where, options = {}) { for (const [field, condition] of Object.entries(where)) { // 顶层 $and if (field === '$and') { const subs = condition; if (!subs.every((sub) => matchWhere(row, sub, options))) return false; continue; } // 顶层 $or if (field === '$or') { const subs = condition; if (!subs.some((sub) => matchWhere(row, sub, options))) return false; continue; } if (!matchField(row[field], condition, row, options)) return false; } return true; } // --------------------------------------------------------------------------- // 字段匹配 // --------------------------------------------------------------------------- function matchField(value, condition, row, options) { // 嵌套 $and if (typeof condition === 'object' && condition !== null && '$and' in condition) { const subs = condition.$and; return subs.every((sub) => matchWhere(row, sub, options)); } // 嵌套 $or if (typeof condition === 'object' && condition !== null && '$or' in condition) { const subs = condition.$or; return subs.some((sub) => matchWhere(row, sub, options)); } // $not if (typeof condition === 'object' && condition !== null && '$not' in condition) { return !matchField(value, condition.$not, row, options); } // 简单值 => $eq if (typeof condition !== 'object' || condition === null || Array.isArray(condition)) { return value === condition; } const ops = condition; // $col 简写: { $col: name } === { $eq: { $col: name } }(仅 JOIN ON 场景) if (options.$col && '$col' in ops && Object.keys(ops).length === 1) { return value === row[ops.$col]; } // 遍历操作符 for (const [op, operand] of Object.entries(ops)) { let actualOperand = operand; // $col 列引用解析 if (options.$col && typeof operand === 'object' && operand !== null && '$col' in operand) { actualOperand = row[operand.$col]; } if (!matchOperator(value, op, actualOperand)) return false; } return true; } // --------------------------------------------------------------------------- // 操作符匹配 // --------------------------------------------------------------------------- function matchOperator(value, op, operand) { switch (op) { case '$eq': return value === operand; case '$ne': return value !== operand; case '$gt': return value > operand; case '$gte': return value >= operand; case '$lt': return value < operand; case '$lte': return value <= operand; case '$in': return Array.isArray(operand) && operand.includes(value); case '$nin': return Array.isArray(operand) && !operand.includes(value); case '$like': return compileLikeRegex(String(operand)).test(String(value)); default: return true; } } // --------------------------------------------------------------------------- // 排序 // --------------------------------------------------------------------------- function applyOrderBy(rows, orderBy) { return [...rows].sort((a, b) => { for (const { column, direction } of orderBy) { const cmp = compare(a[column], b[column]); if (cmp !== 0) return direction === 'desc' ? -cmp : cmp; } return 0; }); } function compare(a, b) { if (a === b) return 0; if (a === null || a === undefined) return 1; if (b === null || b === undefined) return -1; if (typeof a === 'string' && typeof b === 'string') return a.localeCompare(b); if (typeof a === 'number' && typeof b === 'number') return a - b; return String(a).localeCompare(String(b)); } // --------------------------------------------------------------------------- // 列投影 // --------------------------------------------------------------------------- function projectColumns(row, columns) { const projected = {}; for (const col of columns) { if (col in row) { projected[col] = row[col]; } else { for (const key of Object.keys(row)) { if (key.endsWith(`.${col}`) || key === col) { projected[col] = row[key]; break; } } } } return projected; } /** * metona-sqlark Memory Engine — 基于 Map 的内存存储引擎 * @module engine/memory */ class MemoryEngine { constructor() { this.name = 'memory'; this.tables = new Map(); this.schemas = new Map(); this.indexes = new Map(); this.opened = false; } // ---- 生命周期 ---- async open(_dbName, _version) { this.opened = true; } async close() { this.tables.clear(); this.schemas.clear(); this.indexes.clear(); this.opened = false; } isOpen() { return this.opened; } // ---- 表管理 ---- async createTable(schema) { if (this.schemas.has(schema.name)) throw new DatabaseError(`Table "${schema.name}" already exists`, 'TABLE_EXISTS'); this.schemas.set(schema.name, schema); this.tables.set(schema.name, new Map()); const tableIndexes = new Map(); for (const [colName, colDef] of Object.entries(schema.columns)) { if (colDef.index || colDef.unique) tableIndexes.set(colName, new Map()); } this.indexes.set(schema.name, tableIndexes); } async dropTable(tableName) { this.ensureTable(tableName); this.schemas.delete(tableName); this.tables.delete(tableName); this.indexes.delete(tableName); } async hasTable(tableName) { return this.schemas.has(tableName); } async getTableNames() { return Array.from(this.schemas.keys()); } async getTableSchema(tableName) { return this.schemas.get(tableName) ?? null; } // ---- CRUD ---- async insert(tableName, rows) { this.ensureTable(tableName); const schema = this.schemas.get(tableName); const table = this.tables.get(tableName); const pkColumn = this.getPrimaryKey(schema); const pks = []; for (const row of rows) { const validatedRow = this.validateRow(schema, row); const pkValue = String(validatedRow[pkColumn]); if (table.has(pkValue)) throw new DatabaseError(`Duplicate primary key "${pkValue}" in table "${tableName}"`, 'DUPLICATE_KEY'); this.checkUniqueness(schema, validatedRow); table.set(pkValue, validatedRow); this.updateIndexes(tableName, validatedRow, pkValue); pks.push(pkValue); } return pks; } async find(tableName, query) { this.ensureTable(tableName); const table = this.tables.get(tableName); let results = this.tryIndexLookup(tableName, table, query); if (query.where && Object.keys(query.where).length > 0) { results = results.filter((row) => matchWhere(row, query.where)); } if (query.orderBy && query.orderBy.length > 0) { results = applyOrderBy(results, query.orderBy); } const offset = query.offset ?? 0; const limit = query.limit ?? results.length; results = results.slice(offset, offset + limit); if (query.columns && query.columns.length > 0 && query.columns[0] !== '*') { results = results.map((row) => projectColumns(row, query.columns)); } return results; } async update(tableName, query, updates) { this.ensureTable(tableName); const schema = this.schemas.get(tableName); const table = this.tables.get(tableName); let count = 0; for (const [pk, row] of table) { if (!query.where || Object.keys(query.where).length === 0 || matchWhere(row, query.where)) { const updated = { ...row, ...updates }; this.validateRow(schema, updated); table.set(pk, updated); count++; } } return count; } async delete(tableName, query) { this.ensureTable(tableName); const table = this.tables.get(tableName); const toDelete = []; for (const [pk, row] of table) { if (!query.where || Object.keys(query.where).length === 0 || matchWhere(row, query.where)) { toDelete.push(pk); } } for (const pk of toDelete) table.delete(pk); return toDelete.length; } async count(tableName, query) { this.ensureTable(tableName); const table = this.tables.get(tableName); if (!query?.where || Object.keys(query.where).length === 0) return table.size; let result = 0; for (const row of table.values()) { if (matchWhere(row, query.where)) result++; } return result; } async clear(tableName) { this.ensureTable(tableName); this.tables.get(tableName).clear(); const tableIndexes = this.indexes.get(tableName); if (tableIndexes) for (const colIndex of tableIndexes.values()) colIndex.clear(); } // ---- 内部辅助 ---- ensureTable(tableName) { if (!this.tables.has(tableName)) throw new DatabaseError(`Table "${tableName}" does not exist`, 'TABLE_NOT_FOUND'); } getPrimaryKey(schema) { for (const [name, col] of Object.entries(schema.columns)) { if (col.primaryKey) return name; } return Object.keys(schema.columns)[0]; } validateRow(schema, row) { const validated = {}; for (const [colName, colDef] of Object.entries(schema.columns)) { let value = row[colName]; if (value === undefined && colDef.default !== undefined) value = colDef.default; if (colDef.required && (value === undefined || value === null)) { throw new DatabaseError(`Column "${colName}" is required in table "${schema.name}"`, 'VALIDATION_ERROR'); } if (value !== undefined && value !== null) this.checkType(colName, colDef.type, value); if (value !== undefined) validated[colName] = value; } return validated; } checkType(colName, type, value) { const jsType = typeof value; switch (type) { case 'string': if (jsType !== 'string') throw new DatabaseError(`Column "${colName}" expects string, got ${jsType}`, 'TYPE_ERROR'); break; case 'number': if (jsType !== 'number') throw new DatabaseError(`Column "${colName}" expects number, got ${jsType}`, 'TYPE_ERROR'); break; case 'boolean': if (jsType !== 'boolean') throw new DatabaseError(`Column "${colName}" expects boolean, got ${jsType}`, 'TYPE_ERROR'); break; case 'date': if (jsType !== 'string' || isNaN(Date.parse(value))) throw new DatabaseError(`Column "${colName}" expects valid date`, 'TYPE_ERROR'); break; case 'json': if (jsType !== 'object') throw new DatabaseError(`Column "${colName}" expects object/array, got ${jsType}`, 'TYPE_ERROR'); break; } } /** O(1) 唯一性检查:利用哈希索引 */ checkUniqueness(schema, row) { const tableIndexes = this.indexes.get(schema.name); if (!tableIndexes) return; for (const [colName, colDef] of Object.entries(schema.columns)) { if (!colDef.unique || row[colName] === undefined || row[colName] === null) continue; const colIndex = tableIndexes.get(colName); if (colIndex && colIndex.has(row[colName])) { throw new DatabaseError(`Unique constraint violation on column "${colName}" in table "${schema.name}"`, 'UNIQUE_VIOLATION'); } } } /** 索引查找 */ tryIndexLookup(tableName, table, query) { const tableIndexes = this.indexes.get(tableName); if (!tableIndexes || !query.where) return Array.from(table.values()); for (const [col, condition] of Object.entries(query.where)) { if (typeof condition !== 'object' || condition === null) { const colIndex = tableIndexes.get(col); if (colIndex) { const pks = colIndex.get(condition); if (pks) { const result = []; for (const pk of pks) { const r = table.get(pk); if (r) result.push(r); } return result; } return []; } } } return Array.from(table.values()); } /** 更新索引 */ updateIndexes(tableName, row, pk) { const tableIndexes = this.indexes.get(tableName); if (!tableIndexes) return; for (const [colName, colIndex] of tableIndexes) { const value = row[colName]; if (value !== undefined && value !== null) { if (!colIndex.has(value)) colIndex.set(value, new Set()); colIndex.get(value).add(pk); } } } } /** * metona-sqlark IndexedDB Engine — 基于 IndexedDB 的持久化存储引擎 * @module engine/indexeddb */ class IndexedDBEngine { constructor() { this.name = 'indexeddb'; this.db = null; this.dbName = ''; this.version = 1; this.memoryCache = new MemoryEngine(); } async open(dbName, version) { this.dbName = dbName; this.version = version; await this.memoryCache.open(dbName, version); return new Promise((resolve, reject) => { const request = indexedDB.open(dbName, version); request.onsuccess = () => { this.db = request.result; resolve(); }; request.onerror = () => reject(new DatabaseError(`Failed to open IndexedDB "${dbName}"`, 'IDB_OPEN_ERROR', request.error)); request.onblocked = () => reject(new DatabaseError(`IndexedDB "${dbName}" is blocked`, 'IDB_BLOCKED')); }); } async close() { if (this.db) { this.db.close(); this.db = null; } await this.memoryCache.close(); } isOpen() { return this.db !== null; } // ---- 表管理 ---- async createTable(schema) { await this.memoryCache.createTable(schema); const db = this.ensureDB(); return new Promise((resolve, reject) => { const newVersion = db.version + 1; db.close(); const request = indexedDB.open(this.dbName, newVersion); request.onupgradeneeded = (event) => { const db = event.target.result; const pkColumn = Object.entries(schema.columns).find(([, c]) => c.primaryKey)?.[0] ?? Object.keys(schema.columns)[0]; const store = db.createObjectStore(schema.name, { keyPath: pkColumn }); for (const [colName, colDef] of Object.entries(schema.columns)) { if (colDef.index && colName !== pkColumn) { store.createIndex(`idx_${colName}`, colName, { unique: colDef.unique ?? false }); } } }; request.onsuccess = () => { this.db = request.result; resolve(); }; request.onerror = () => reject(new DatabaseError(`Failed to create table "${schema.name}"`, 'IDB_UPGRADE_ERROR', request.error)); }); } async dropTable(tableName) { await this.memoryCache.dropTable(tableName); const db = this.ensureDB(); return new Promise((resolve, reject) => { const newVersion = db.version + 1; db.close(); const request = indexedDB.open(this.dbName, newVersion); request.onupgradeneeded = (event) => { const db = event.target.result; if (db.objectStoreNames.contains(tableName)) db.deleteObjectStore(tableName); }; request.onsuccess = () => { this.db = request.result; resolve(); }; request.onerror = () => reject(new DatabaseError(`Failed to drop table "${tableName}"`, 'IDB_UPGRADE_ERROR', request.error)); }); } async hasTable(tableName) { return this.ensureDB().objectStoreNames.contains(tableName); } async getTableNames() { return Array.from(this.ensureDB().objectStoreNames); } async getTableSchema(tableName) { return this.memoryCache.getTableSchema(tableName); } // ---- CRUD ---- async insert(tableName, rows) { await this.memoryCache.insert(tableName, rows); const db = this.ensureDB(); return new Promise((resolve, reject) => { const tx = db.transaction(tableName, 'readwrite'); const store = tx.objectStore(tableName); const pks = []; for (const row of rows) { const req = store.add(row); req.onsuccess = () => pks.push(String(req.result)); req.onerror = () => { }; // 内存层已校验,忽略 IDB 重复键 } tx.oncomplete = () => resolve(pks); tx.onerror = () => reject(new DatabaseError(`Insert failed for "${tableName}"`, 'IDB_TX_ERROR', tx.error)); }); } async find(tableName, query) { const db = this.ensureDB(); return new Promise((resolve, reject) => { const tx = db.transaction(tableName, 'readonly'); const req = tx.objectStore(tableName).getAll(); req.onsuccess = () => { let results = req.result ?? []; if (query.where && Object.keys(query.where).length > 0) { results = results.filter((row) => matchWhere(row, query.where)); } if (query.orderBy && query.orderBy.length > 0) { results = applyOrderBy(results, query.orderBy); } const offset = query.offset ?? 0; const limit = query.limit ?? results.length; results = results.slice(offset, offset + limit); if (query.columns && query.columns.length > 0 && query.columns[0] !== '*') { results = results.map((r) => projectColumns(r, query.columns)); } resolve(results); }; req.onerror = () => reject(new DatabaseError(`Find failed for "${tableName}"`, 'IDB_READ_ERROR', req.error)); }); } async update(tableName, query, updates) { await this.memoryCache.update(tableName, query, updates); const db = this.ensureDB(); return new Promise((resolve, reject) => { const tx = db.transaction(tableName, 'readwrite'); const store = tx.objectStore(tableName); const getAllReq = store.getAll(); let count = 0; getAllReq.onsuccess = () => { for (const row of getAllReq.result ?? []) { if (!query.where || Object.keys(query.where).length === 0 || matchWhere(row, query.where)) { Object.assign(row, updates); store.put(row); count++; } } }; tx.oncomplete = () => resolve(count); tx.onerror = () => reject(new DatabaseError(`Update failed for "${tableName}"`, 'IDB_TX_ERROR', tx.error)); }); } async delete(tableName, query) { await this.memoryCache.delete(tableName, query); const db = this.ensureDB(); const schema = await this.memoryCache.getTableSchema(tableName); if (!schema) throw new DatabaseError(`Table "${tableName}" not found`, 'TABLE_NOT_FOUND'); const pkColumn = Object.entries(schema.columns).find(([, c]) => c.primaryKey)?.[0] ?? Object.keys(schema.columns)[0]; return new Promise((resolve, reject) => { const tx = db.transaction(tableName, 'readwrite'); const store = tx.objectStore(tableName); const getAllReq = store.getAll(); let count = 0; getAllReq.onsuccess = () => { for (const row of getAllReq.result ?? []) { if (!query.where || Object.keys(query.where).length === 0 || matchWhere(row, query.where)) { store.delete(row[pkColumn]); count++; } } }; tx.oncomplete = () => resolve(count); tx.onerror = () => reject(new DatabaseError(`Delete failed for "${tableName}"`, 'IDB_TX_ERROR', tx.error)); }); } async count(tableName, query) { const results = await this.find(tableName, { table: tableName, where: query?.where ?? {} }); return results.length; } async clear(tableName) { await this.memoryCache.clear(tableName); const db = this.ensureDB(); return new Promise((resolve, reject) => { const tx = db.transaction(tableName, 'readwrite'); tx.objectStore(tableName).clear(); tx.oncomplete = () => resolve(); tx.onerror = () => reject(new DatabaseError(`Clear failed for "${tableName}"`, 'IDB_TX_ERROR', tx.error)); }); } ensureDB() { if (!this.db) throw new DatabaseError('Database not opened', 'DB_NOT_OPEN'); return this.db; } } /** * metona-sqlark OPFS Engine — 基于 Origin Private File System 的持久化存储引擎 * @module engine/opfs * * 使用 JSON-per-table 文件存储方案。 * 目录结构:{dbName}/tables/{tableName}.json */ // --------------------------------------------------------------------------- // OPFSEngine // --------------------------------------------------------------------------- class OPFSEngine { constructor() { this.name = 'opfs'; this.root = null; this.tablesDir = null; this.dbName = ''; // 运行时内存缓存(OPFS 文件读写有延迟) this.memoryCache = new MemoryEngine(); } // ---- 生命周期 ---- async open(dbName, version) { this.dbName = dbName; await this.memoryCache.open(dbName, version); // 获取 OPFS 根目录 this.root = await navigator.storage.getDirectory(); // 创建数据库目录 this.tablesDir = await this.root.getDirectoryHandle(dbName, { create: true }); } async close() { this.root = null; this.tablesDir = null; await this.memoryCache.close(); } isOpen() { return this.tablesDir !== null; } // ---- 表管理 ---- async createTable(schema) { await this.memoryCache.createTable(schema); // OPFS 中表以空 JSON 数组文件形式存在 await this.writeTableData(schema.name, []); } async dropTable(tableName) { await this.memoryCache.dropTable(tableName); if (this.tablesDir) { try { await this.tablesDir.removeEntry(`${tableName}.json`); } catch { // 文件不存在则忽略 } } } async hasTable(tableName) { if (!this.tablesDir) return false; try { await this.tablesDir.getFileHandle(`${tableName}.json`); return true; } catch { return false; } } async getTableNames() { if (!this.tablesDir) return []; const names = []; for await (const [name] of this.tablesDir.entries()) { if (name.endsWith('.json')) { names.push(name.replace('.json', '')); } } return names; } async getTableSchema(tableName) { return this.memoryCache.getTableSchema(tableName); } // ---- CRUD ---- async insert(tableName, rows) { const pks = await this.memoryCache.insert(tableName, rows); // 持久化到 OPFS const allRows = await this.memoryCache.find(tableName, { table: tableName }); await this.writeTableData(tableName, allRows); return pks; } async find(tableName, query) { return this.memoryCache.find(tableName, query); } async update(tableName, query, updates) { const count = await this.memoryCache.update(tableName, query, updates); const allRows = await this.memoryCache.find(tableName, { table: tableName }); await this.writeTableData(tableName, allRows); return count; } async delete(tableName, query) { const count = await this.memoryCache.delete(tableName, query); const allRows = await this.memoryCache.find(tableName, { table: tableName }); await this.writeTableData(tableName, allRows); return count; } async count(tableName, query) { return this.memoryCache.count(tableName, query); } async clear(tableName) { await this.memoryCache.clear(tableName); await this.writeTableData(tableName, []); } // ---- 内部辅助 ---- ensureDir() { if (!this.tablesDir) { throw new DatabaseError('Database not opened', 'DB_NOT_OPEN'); } return this.tablesDir; } async writeTableData(tableName, data) { const dir = this.ensureDir(); const fileName = `${tableName}.json`; const fileHandle = await dir.getFileHandle(fileName, { create: true }); const writable = await fileHandle.createWritable(); await writable.write(JSON.stringify(data)); await writable.close(); } async readTableData(tableName) { const dir = this.ensureDir(); const fileName = `${tableName}.json`; try { const fileHandle = await dir.getFileHandle(fileName); const file = await fileHandle.getFile(); const text = await file.text(); return JSON.parse(text); } catch { return []; } } /** 从 OPFS 加载表数据到内存缓存 */ async loadTableIntoMemory(tableName, schema) { await this.memoryCache.createTable(schema); const rows = await this.readTableData(tableName); if (rows.length > 0) { // 直接用 Map 设置绕过 insert 校验 for (const row of rows) { await this.memoryCache.insert(tableName, [row]); } } } } /** * metona-sqlark Hybrid Engine — 内存 + 磁盘混合存储引擎 * @module hybrid/index * * 采用 write-through 策略: * - 所有写操作同时写入内存和磁盘 * - 所有读操作直接从内存返回 * - 数据库打开时从磁盘加载数据到内存 */ // --------------------------------------------------------------------------- // HybridEngine // --------------------------------------------------------------------------- class HybridEngine { constructor(diskEngine = 'indexeddb') { this.name = 'hybrid'; this.memoryEngine = new MemoryEngine(); this.diskEngineType = diskEngine; this.diskEngine = diskEngine === 'opfs' ? new OPFSEngine() : new IndexedDBEngine(); } // ---- 生命周期 ---- async open(dbName, version) { // 先打开磁盘引擎 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() { await this.memoryEngine.close(); await this.diskEngine.close(); } isOpen() { return this.memoryEngine.isOpen() && this.diskEngine.isOpen(); } // ---- 表管理 ---- async createTable(schema) { await this.memoryEngine.createTable(schema); await this.diskEngine.createTable(schema); } async dropTable(tableName) { await this.memoryEngine.dropTable(tableName); await this.diskEngine.dropTable(tableName); } async hasTable(tableName) { return this.memoryEngine.hasTable(tableName); } async getTableNames() { return this.memoryEngine.getTableNames(); } async getTableSchema(tableName) { return this.memoryEngine.getTableSchema(tableName); } // ---- CRUD(write-through 策略) ---- async insert(tableName, rows) { const pks = await this.memoryEngine.insert(tableName, rows); // write-through: 同步写入磁盘 await this.diskEngine.insert(tableName, rows); return pks; } async find(tableName, query) { // 直接从内存读取 return this.memoryEngine.find(tableName, query); } async update(tableName, query, updates) { const count = await this.memoryEngine.update(tableName, query, updates); // write-through: 同步更新磁盘 await this.diskEngine.update(tableName, query, updates); return count; } async delete(tableName, query) { const count = await this.memoryEngine.delete(tableName, query); // write-through: 同步删除磁盘 await this.diskEngine.delete(tableName, query); return count; } async count(tableName, query) { return this.memoryEngine.count(tableName, query); } async clear(tableName) { await this.memoryEngine.clear(tableName); await this.diskEngine.clear(tableName); } // ---- 引擎信息 ---- /** 获取磁盘引擎类型 */ getDiskEngineType() { return this.diskEngineType; } /** 获取内存引擎(供内部使用) */ getMemoryEngine() { return this.memoryEngine; } } /** * metona-sqlark Query Builder — 链式查询构建器 * @module query/builder * * 链式调用 → 构建 AST → 执行引擎操作。 * 支持 JOIN(需要 Executor)。 */ // --------------------------------------------------------------------------- // SelectQueryBuilder // --------------------------------------------------------------------------- class SelectQueryBuilder { constructor(engine, tableName, _columns = ['*'], executor) { this.engine = engine; this.tableName = tableName; this._columns = _columns; this._where = {}; this._orderBy = []; this._joins = []; this._executor = executor; } /** 主表别名 */ as(alias) { this._alias = alias; return this; } /** INNER JOIN */ innerJoin(table, on, alias) { return this._addJoin('INNER', table, on, alias); } /** LEFT JOIN */ leftJoin(table, on, alias) { return this._addJoin('LEFT', table, on, alias); } /** RIGHT JOIN */ rightJoin(table, on, alias) { return this._addJoin('RIGHT', table, on, alias); } /** CROSS JOIN */ crossJoin(table, alias) { return this._addJoin('CROSS', table, {}, alias); } /** 通用 JOIN */ join(table, on, alias) { return this._addJoin('INNER', table, on, alias); } _addJoin(type, table, on, alias) { this._joins.push({ type, table, on, alias }); return this; } /** 添加过滤条件 */ where(condition) { this._where = { ...this._where, ...condition }; return this; } /** 排序 */ orderBy(column, direction = 'asc') { this._orderBy.push({ column, direction }); return this; } /** 限制返回条数 */ limit(n) { this._limit = n; return this; } /** 偏移量 */ offset(n) { this._offset = n; return this; } /** 执行查询 */ async execute() { // 有 JOIN → 通过 Executor 执行 if (this._joins.length > 0 && this._executor) { const ast = this.toAST(); return this._executor.execute(ast); } // 无 JOIN → 直接调用引擎 return this.engine.find(this.tableName, { table: this.tableName, columns: this._columns, where: this._where, orderBy: this._orderBy.length > 0 ? this._orderBy : undefined, limit: this._limit, offset: this._offset, }); } /** 获取 AST */ toAST() { return { type: 'SELECT', columns: this._columns, from: this.tableName, alias: this._alias, joins: this._joins.length > 0 ? [...this._joins] : undefined, where: this._where, orderBy: this._orderBy.length > 0 ? this._orderBy : undefined, limit: this._limit, offset: this._offset, }; } } // --------------------------------------------------------------------------- // UpdateQueryBuilder // --------------------------------------------------------------------------- class UpdateQueryBuilder { constructor(engine, tableName, _updates) { this.engine = engine; this.tableName = tableName; this._updates = _updates; this._where = {}; } where(condition) { this._where = { ...this._where, ...condition }; return this; } async execute() { return this.engine.update(this.tableName, { table: this.tableName, where: this._where }, this._updates); } toAST() { return { type: 'UPDATE', table: this.tableName, sets: this._updates, where: this._where }; } } // --------------------------------------------------------------------------- // DeleteQueryBuilder // --------------------------------------------------------------------------- class DeleteQueryBuilder { constructor(engine, tableName) { this.engine = engine; this.tableName = tableName; this._where = {}; } where(condition) { this._where = { ...this._where, ...condition }; return this; } async execute() { return this.engine.delete(this.tableName, { table: this.tableName, where: this._where }); } toAST() { return { type: 'DELETE', from: this.tableName, where: this._where }; } } /** * metona-sqlark Table — 表操作 API * @module table/table */ // --------------------------------------------------------------------------- // Table // --------------------------------------------------------------------------- class Table { constructor(engine, tableName, executor) { this.schema = null; this.engine = engine; this.name = tableName; this.executor = executor; } // ---- Schema ---- async getSchema() { if (!this.schema) { const s = await this.engine.getTableSchema(this.name); if (!s) throw new DatabaseError(`Table "${this.name}" does not exist`, 'TABLE_NOT_FOUND'); this.schema = s; } return this.schema; } // ---- 插入 ---- async insert(row) { const pks = await this.engine.insert(this.name, [row]); return pks[0]; } async insertMany(rows) { return this.engine.insert(this.name, rows); } // ---- 查询 ---- select(columns = ['*']) { return new SelectQueryBuilder(this.engine, this.name, columns, this.executor); } // ---- 更新 ---- update(updates) { return new UpdateQueryBuilder(this.engine, this.name, updates); } // ---- 删除 ---- delete() { return new DeleteQueryBuilder(this.engine, this.name); } // ---- 聚合 ---- async count(where) { return this.engine.count(this.name, where ? { table: this.name, where } : undefined); } // ---- 管理 ---- async clear() { return this.engine.clear(this.name); } async drop() { return this.engine.dropTable(this.name); } } /** * metona-sqlark Schema — 表结构定义与校验 * @module table/schema */ // --------------------------------------------------------------------------- // Schema 工具 // --------------------------------------------------------------------------- /** 从列定义创建 TableSchema */ function createSchema(name, columns) { validateColumns(columns); return { name, columns }; } /** 校验列定义 */ function validateColumns(columns) { const colNames = Object.keys(columns); if (colNames.length === 0) { throw new DatabaseError('Table must have at least one column', 'SCHEMA_ERROR'); } let primaryKeyCount = 0; for (const [colName, colDef] of Object.entries(columns)) { // 类型校验 if (!FIELD_TYPES.includes(colDef.type)) { throw new DatabaseError(`Invalid type "${colDef.type}" for column "${colName}". Valid types: ${FIELD_TYPES.join(', ')}`, 'SCHEMA_ERROR'); } // 主键计数 if (colDef.primaryKey) { primaryKeyCount++; } } // 至少需要一个主键 if (primaryKeyCount === 0) { throw new DatabaseError('Table must have at least one primary key column', 'SCHEMA_ERROR'); } } /** 将 AST 列定义转换为 ColumnDef */ function astColumnToColumnDef(astCol) { return { type: astCol.type, primaryKey: astCol.primaryKey, unique: astCol.unique, required: astCol.required, default: astCol.default, index: astCol.index, maxLength: astCol.maxLength, min: astCol.min, max: astCol.max, }; } /** * metona-sqlark Query Compiler — AST → 查询计划 * @module query/compiler * * 将 AST 语句编译为引擎可执行的 QueryPlan。 * v0.0.1: 简单直接映射,未来可加入索引选择、过滤下推等优化。 */ // --------------------------------------------------------------------------- // 编译 AST → QueryPlan // --------------------------------------------------------------------------- /** * 编译 SELECT / DELETE / UPDATE 语句为 QueryPlan。 * INSERT 和 DDL 语句不需要 QueryPlan。 */ function compileStatement(stmt) { switch (stmt.type) { case 'SELECT': return compileSelect(stmt); case 'DELETE': return compileDelete(stmt); case 'UPDATE': return compileUpdate(stmt); default: throw new DatabaseError(`Cannot compile statement type "${stmt.type}" to QueryPlan`, 'COMPILE_ERROR'); } } function compileSelect(stmt) { return { table: stmt.from, columns: stmt.columns, where: stmt.where, orderBy: stmt.orderBy?.length ? stmt.orderBy : undefined, limit: stmt.limit, offset: stmt.offset, }; } function compileDelete(stmt) { return { table: stmt.from, where: stmt.where, }; } function compileUpdate(stmt) { return { table: stmt.table, where: stmt.where, }; } /** * metona-sqlark Query Executor — AST 执行器 * @module query/executor * * JOIN / GROUP BY / DISTINCT 逻辑在此层处理。 */ // --------------------------------------------------------------------------- // Executor // --------------------------------------------------------------------------- class QueryExecutor { constructor(engine) { this.engine = engine; } async execute(stmt) { switch (stmt.type) { case 'SELECT': return this.executeSelect(stmt); case 'INSERT': return this.executeInsert(stmt); case 'UPDATE': return this.executeUpdate(stmt); case 'DELETE': return this.executeDelete(stmt); case 'CREATE_TABLE': return this.executeCreateTable(stmt); case 'DROP_TABLE': return this.executeDropTable(stmt); default: throw new DatabaseError('Unknown statement type', 'UNKNOWN_STATEMENT'); } } // =================================================================== // SELECT // =================================================================== async executeSelect(stmt) { const hasGroupBy = !!(stmt.groupBy && stmt.groupBy.length > 0); let rows; if (!stmt.joins || stmt.joins.length === 0) { const plan = compileStatement(hasGroupBy ? { ...stmt, columns: ['*'] } : stmt); rows = await this.engine.find(plan.table, plan); } else { rows = await this.executeJoinSelect(stmt); } if (hasGroupBy) rows = this.executeGroupBy(rows, stmt); if (stmt.distinct) rows = this.executeDistinct(rows); if (stmt.having && Object.keys(stmt.having).length > 0) { rows = rows.filter((row) => matchWhere(row, stmt.having)); } if (stmt.orderBy && stmt.orderBy.length > 0) rows = applyOrderBy(rows, stmt.orderBy); const offset = stmt.offset ?? 0; const limit = stmt.limit ?? rows.length; rows = rows.slice(offset, offset + limit); if (!hasGroupBy && stmt.columns.length > 0 && stmt.columns[0] !== '*') { rows = rows.map((row) => projectColumns(row, stmt.columns)); } return rows; } // ---- JOIN ---- async executeJoinSelect(stmt) { const mainAlias = stmt.alias ?? stmt.from; const mainRows = (await this.engine.find(stmt.from, { table: stmt.from })) .map((row) => this.prefixRow(row, mainAlias)); let resultRows = mainRows; for (const join of stmt.joins) { const joinAlias = join.alias ?? join.table; const joinRows = (await this.engine.find(join.table, { table: join.table })) .map((row) => this.prefixRow(row, joinAlias)); resultRows = this.joinRows(resultRows, joinRows, join); } if (stmt.where && Object.keys(stmt.where).length > 0) { resultRows = resultRows.filter((row) => matchWhere(row, stmt.where)); } return resultRows; } prefixRow(row, alias) { const prefixed = {}; for (const [key, value] of Object.entries(row)) prefixed[`${alias}.${key}`] = value; return prefixed; } /** 嵌套循环连接(优化:避免 ON 时对象扩散) */ joinRows(leftRows, rightRows, join) { if (join.type === 'CROSS') { const result = []; for (const l of leftRows) for (const r of rightRows) result.push({ ...l, ...r }); return result; } const result = []; for (const l of leftRows) { let matched = false; for (const r of rightRows) { // 合并后匹配 ON(避免创建临时对象再丢弃) const merged = { ...l, ...r }; if (matchWhere(merged, join.on, { $col: true })) { result.push(merged); matched = true; } } if (!matched && join.type === 'LEFT') { const nullRight = {}; for (const key of Object.keys(rightRows[0] ?? {})) nullRight[key] = null; result.push({ ...l, ...nullRight }); } } if (join.type === 'RIGHT') { for (const r of rightRows) { const isMatched = leftRows.some((l) => { const merged = { ...l, ...r }; return matchWhere(merged, join.on, { $col: true }); }); if (!isMatched) { const nullLeft = {}; for (const key of Object.keys(leftRows[0] ?? {})) nullLeft[key] = null; result.push({ ...nullLeft, ...r }); } } } return result; } // ---- GROUP BY ---- executeGroupBy(rows, stmt) { const groups = new Map(); for (const row of rows) { const key = stmt.groupBy.map((col) => String(row[col] ?? 'null')).join('|'); if (!groups.has(key)) groups.set(key, []); groups.get(key).push(row); } const result = []; for (const groupRows of groups.values()) { const aggregated = {}; for (const col of stmt.groupBy) aggregated[col] = groupRows[0][col]; for (const colExpr of stmt.columns) { if (colExpr === '*') continue; const m = colExpr.match(/^(COUNT|SUM|AVG|MIN|MAX)\((.+?)\)(?:\s+AS\s+(\w+))?$/i); if (m) { const [, func, arg, alias] = m; aggregated[alias || colExpr] = this.computeAggregate(func.toUpperCase(), groupRows, arg.trim()); } else if (!stmt.groupBy.includes(colExpr)) { aggregated[colExpr] = groupRows[0][colExpr]; } } result.push(aggregated); } return result; } computeAggregate(func, rows, col) { const nums = rows.map((r) => r[col]).filter((v) => v !== null && v !== undefined).map(Number); switch (func) { case 'COUNT': return col === '*' ? rows.length : nums.length; case 'SUM': return nums.reduce((a, b) => a + b, 0); case 'AVG': return nums.length === 0 ? 0 : nums.reduce((a, b) => a + b, 0) / nums.length; case 'MIN': return nums.length === 0 ? 0 : Math.min(...nums); case 'MAX': return nums.length === 0 ? 0 : Math.max(...nums); default: return 0; } } // ---- DISTINCT(优化:列值拼接代替 JSON.stringify) ---- executeDistinct(rows) { const seen = new Set(); return rows.filter((row) => { const key = Object.values(row).map((v) => String(v ?? '\0')).join('\x1f'); if (seen.has(key)) return false; seen.add(key); return true; }); } // =================================================================== // 其他语句 // =================================================================== async executeInsert(stmt) { const schema = await this.engine.getTableSchema(stmt.into); if (!schema) throw new DatabaseError(`Table "${stmt.into}" does not exist`, 'TABLE_NOT_FOUND'); const colNames = stmt.columns ?? Object.keys(schema.columns); const rows = stmt.values.map((vals) => { const row = {}; for (let i = 0; i < colNames.length; i++) { if (i < vals.length) row[colNames[i]] = vals[i]; } return row; }); return this.engine.insert(stmt.into, rows); } async executeUpdate(stmt) { const plan = compileStatement(stmt); return this.engine.update(plan.table, plan, stmt.sets); } async executeDelete(stmt) { const plan = compileStatement(stmt); return this.engine.delete(plan.table, plan); } async executeCreateTable(stmt) { const columns = {}; for (const col of stmt.columns) columns[col.name] = astColumnToColumnDef(col); return this.engine.createTable(createSchema(stmt.name, columns)); } async executeDropTable(stmt) { return this.engine.dropTable(stmt.name); } getEngine() { return this.engine; } } /** * metona-sqlark SQL Token Types — 词法单元定义 * @module sql/tokens */ // --------------------------------------------------------------------------- // Token 类型枚举 // --------------------------------------------------------------------------- var TokenType; (function (TokenType) { // 关键字 TokenType["SELECT"] = "SELECT"; TokenType["FROM"] = "FROM"; TokenType["WHERE"] = "WHERE"; TokenType["INSERT"] = "INSERT"; TokenType["INTO"] = "INTO"; TokenType["VALUES"] = "VALUES"; TokenType["UPDATE"] = "UPDATE"; TokenType["SET"] = "SET"; TokenType["DELETE"] = "DELETE"; TokenType["CREATE"] = "CREATE"; TokenType["TABLE"] = "TABLE"; TokenType["DROP"] = "DROP"; TokenType["ORDER"] = "ORDER"; TokenType["BY"] = "BY"; TokenType["ASC"] = "ASC"; TokenType["DESC"] = "DESC"; TokenType["LIMIT"] = "LIMIT"; TokenType["OFFSET"] = "OFFSET"; TokenType["AND"] = "AND"; TokenType["OR"] = "OR"; TokenType["NOT"] = "NOT"; TokenType["LIKE"] = "LIKE"; TokenType["IN"] = "IN"; TokenType["PRIMARY"] = "PRIMARY"; TokenType["KEY"] = "KEY"; TokenType["UNIQUE"] = "UNIQUE"; TokenType["DEFAULT"] = "DEFAULT"; TokenType["NULL"] = "NULL"; TokenType["TRUE"] = "TRUE"; TokenType["FALSE"] = "FALSE"; // JOIN 相关 TokenType["INNER"] = "INNER"; TokenType["LEFT"] = "LEFT"; TokenType["RIGHT"] = "RIGHT"; TokenType["CROSS"] = "CROSS"; TokenType["JOIN"] = "JOIN"; TokenType["ON"] = "ON"; TokenType["AS"] = "AS"; TokenType["OUTER"] = "OUTER"; // 聚合 TokenType["GROUP"] = "GROUP"; TokenType["HAVING"] = "HAVING"; TokenType["COUNT"] = "COUNT"; TokenType["SUM"] = "SUM"; TokenType["AVG"] = "AVG"; TokenType["MIN"] = "MIN"; TokenType["MAX"] = "MAX"; TokenType["DISTINCT"] = "DISTINCT"; // 标识符 & 字面量 TokenType["IDENTIFIER"] = "IDENTIFIER"; TokenType["STRING"] = "STRING"; TokenType["NUMBER"] = "NUMBER"; // 运算符 & 分隔符 TokenType["COMMA"] = "COMMA"; TokenType["LPAREN"] = "LPAREN"; TokenType["RPAREN"] = "RPAREN"; TokenType["SEMICOLON"] = "SEMICOLON"; TokenType["EQ"] = "EQ"; TokenType["NEQ"] = "NEQ"; TokenType["GT"] = "GT"; TokenType["GTE"] = "GTE"; TokenType["LT"] = "LT"; TokenType["LTE"] = "LTE"; TokenType["STAR"] = "STAR"; TokenType["DOT"] = "DOT"; // 特殊 TokenType["EOF"] = "EOF"; TokenType["ILLEGAL"] = "ILLEGAL"; })(TokenType || (TokenType = {})); // --------------------------------------------------------------------------- // 关键字映射 // --------------------------------------------------------------------------- const KEYWORDS = { 'SELECT': TokenType.SELECT, 'FROM': TokenType.FROM, 'WHERE': TokenType.WHERE, 'INSERT': TokenType.INSERT, 'INTO': TokenType.INTO, 'VALUES': TokenType.VALUES, 'UPDATE': TokenType.UPDATE, 'SET': TokenType.SET, 'DELETE': TokenType.DELETE, 'CREATE': TokenType.CREATE, 'TABLE': TokenType.TABLE, 'DROP': TokenType.DROP, 'ORDER': TokenType.ORDER, 'BY': TokenType.BY, 'ASC': TokenType.ASC, 'DESC': TokenType.DESC, 'LIMIT': TokenType.LIMIT, 'OFFSET': TokenType.OFFSET, 'AND': TokenType.AND, 'OR': TokenType.OR, 'NOT': TokenType.NOT, 'LIKE': TokenType.LIKE, 'IN': TokenType.IN, 'PRIMARY': TokenType.PRIMARY, 'KEY': TokenType.KEY, 'UNIQUE': TokenType.UNIQUE, 'DEFAULT': TokenType.DEFAULT, 'NULL': TokenType.NULL, 'TRUE': TokenType.TRUE, 'FALSE': TokenType.FALSE, // JOIN 'INNER': TokenType.INNER, 'LEFT': TokenType.LEFT, 'RIGHT': TokenType.RIGHT, 'CROSS': TokenType.CROSS, 'JOIN': TokenType.JOIN, 'ON': TokenType.ON, 'AS': TokenType.AS, 'OUTER': TokenType.OUTER, // 聚合 'GROUP': TokenType.GROUP, 'HAVING': TokenType.HAVING, 'COUNT': TokenType.COUNT, 'SUM': TokenType.SUM, 'AVG': TokenType.AVG, 'MIN': TokenType.MIN, 'MAX': TokenType.MAX, 'DISTINCT': TokenType.DISTINCT, }; /** * metona-sqlark SQL Lexer — 词法分析器 * @module sql/lexer * * 将 SQL 字符串切分为 Token 流。 */ // --------------------------------------------------------------------------- // Lexer // --------------------------------------------------------------------------- class Lexer { constructor(input) { this.position = 0; this.readPosition = 0; this.ch = ''; this.input = input; this.readChar(); } /** 读取下一个 Token */ nextToken() { this.skipWhitespace(); let tok; switch (this.ch) { case ',': tok = this.makeToken(TokenType.COMMA, ','); break; case '(': tok = this.makeToken(TokenType.LPAREN, '('); break; case ')': tok = this.makeToken(TokenType.RPAREN, ')'); break; case ';': tok = this.makeToken(TokenType.SEMICOLON, ';'); break; case '*': tok = this.makeToken(TokenType.STAR, '*'); break; case '.': tok = this.makeToken(TokenType.DOT, '.'); break; case '=': tok = this.makeToken(TokenType.EQ, '='); break; case '!': if (this.peekChar() === '=') { this.readChar(); tok = this.makeToken(TokenType.NEQ, '!='); } else { tok = this.makeToken(TokenType.ILLEGAL, '!'); } break; case '>': if (this.peekChar() === '=') { this.readChar(); tok = this.makeToken(TokenType.GTE, '>='); } else { tok = this.makeToken(TokenType.GT, '>'); } break; case '<': if (this.peekChar() === '=') { this.readChar(); tok = this.makeToken(TokenType.LTE, '<='); } else if (this.peekChar() === '>') { this.readChar(); tok = this.makeToken(TokenType.NEQ, '<>'); } else { tok = this.makeToken(TokenType.LT, '<'); } break; case "'": case '"': tok = this.readString(this.ch); break; case '': tok = { type: TokenType.EOF, value: '', position: this.position }; break; default: if (this.isLetter(this.ch)) { const ident = this.readIdentifier(); const keyword = KEYWORDS[ident.toUpperCase()]; tok = { type: keyword ?? TokenType.IDENTIFIER, value: ident, position: this.position - ident.length, }; return tok; // 已读取完毕,不需要再 readChar } else if (this.isDigit(this.ch) || (this.ch === '-' && this.isDigit(this.peekChar()))) { const num = this.readNumber(); tok = { type: TokenType.NUMBER, value: num, position: this.position - num.length, }; return tok; } else { tok = this.makeToken(TokenType.ILLEGAL, this.ch); } break; } this.readChar(); return tok; } // ---- 内部 ---- readChar() { if (this.readPosition >= this.input.length) { this.ch = ''; } else { this.ch = this.input[this.readPosition]; } this.position = this.readPosition; this.readPosition++; } peekChar() { if (this.readPosition >= this.input.length) return ''; return this.input[this.readPosition]; } skipWhitespace() { while (this.ch === ' ' || this.ch === '\t' || this.ch === '\n' || this.ch === '\r') { this.readChar(); } } readIdentifier() { const start = this.position; while (this.isLetter(this.ch) || this.isDigit(this.ch) || this.ch === '_') { this.readChar(); } return this.input.slice(start, this.position); } readNumber() { const start = this.position; // 负号 if (this.ch === '-') this.readChar(); while (this.isDigit(this.ch)) { this.readChar(); } // 小数点 if (this.ch === '.' && this.isDigit(this.peekChar())) { this.readChar(); while (this.isDigit(this.ch)) { this.readChar(); } } return this.input.slice(start, this.position); } readString(quote) { const start = this.position + 1; // 跳过一个引号 this.readChar(); // 跳过开始引号 let value = ''; while (this.ch !== quote && this.ch !== '') { // 处理转义 if (this.ch === '\\' && this.peekChar() === quote) { this.readChar(); value += quote; } else { value += this.ch; } this.readChar(); } // 跳过结束引号(在 readChar 之后才会调用,所以这里不需要处理) return { type: TokenType.STRING, value, position: start, }; } isLetter(ch) { return /[a-zA-Z_]/.test(ch); } isDigit(ch) { return /[0-9]/.test(ch); } makeToken(type, value) { return { type, value, position: this.position }; } } // --------------------------------------------------------------------------- // 便捷方法:一次性词法分析 // --------------------------------------------------------------------------- /** 将 SQL 字符串解析为 Token 列表 */ function tokenize(sql) { const lexer = new Lexer(sql); const tokens = []; let tok = lexer.nextToken(); while (tok.type !== TokenType.EOF) { tokens.push(tok); tok = lexer.nextToken(); } tokens.push(tok); // EOF return tokens; } /** * metona-sqlark SQL Parser — 递归下降语法分析器 * @module sql/parser * * Token 流 → AST Statement。 * 支持的语法是标准 SQL 的子集。 */ // --------------------------------------------------------------------------- // Parser // --------------------------------------------------------------------------- class Parser { constructor(sql) { this.lexer = new Lexer(sql); // 预读两个 token this.nextToken(); this.nextToken(); } /** 解析完整 SQL 语句 */ parseStatement() { switch (this.curToken.type) { case TokenType.SELECT: return this.parseSelect(); case TokenType.INSERT: return this.parseInsert(); case TokenType.UPDATE: return this.parseUpdate(); case TokenType.DELETE: return this.parseDelete(); case TokenType.CREATE: return this.parseCreateTable(); case TokenType.DROP: return this.parseDropTable(); default: throw this.error(`Unexpected token "${this.curToken.value}"`); } } // =================================================================== // SELECT // =================================================================== parseSelect() { this.expect(TokenType.SELECT); // DISTINCT(可选) let distinct = false; if (this.curTokenIs(TokenType.DISTINCT)) { distinct = true; this.nextToken(); } // 列 const columns = []; if (this.curTokenIs(TokenType.STAR)) { columns.push('*'); this.nextToken(); } else { columns.push(...this.parseColumnList()); } // FROM this.expect(TokenType.FROM); const tableName = this.expectIdentifier('table name'); // 表别名(可选) let alias; if (this.curTokenIs(TokenType.AS)) { this.nextToken(); alias = this.expectIdentifier('alias'); } else if (this.curToken.type === TokenType.IDENTIFIER && !this._isReservedAfterFrom()) { alias = this.curToken.value; this.nextToken(); } const stmt = { type: 'SELECT', columns, distinct: distinct || undefined, from: tableName, alias, where: {}, }; // JOIN 子句(可选,支持多个) const joins = this.parseJoinClauses(); if (joins.length > 0) { stmt.joins = joins; } // WHERE(可选) if (this.curTokenIs(TokenType.WHERE)) { this.nextToken(); stmt.where = this.parseCondition(); } // GROUP BY(可选) if (this.curTokenIs(TokenType.GROUP)) { this.nextToken(); this.expect(TokenType.BY); stmt.groupBy = this.parseIdentifierList(); } // HAVING(可选) if (this.curTokenIs(TokenType.HAVING)) { this.nextToken(); stmt.having = this.parseCondition(); } // ORDER BY(可选) if (this.curTokenIs(TokenType.ORDER)) { this.nextToken(); this.expect(TokenType.BY); stmt.orderBy = this.parseOrderByList(); } // LIMIT(可选) if (this.curTokenIs(TokenType.LIMIT)) { this.nextToken(); stmt.limit = this.expectNumber('LIMIT value'); } // OFFSET(可选) if (this.curTokenIs(TokenType.OFFSET)) { this.nextToken(); stmt.offset = this.expectNumber('OFFSET value'); } return stmt; } /** 解析 JOIN 子句列表 */ parseJoinClauses() { const joins = []; while (this._isJoinKeyword()) { joins.push(this.parseJoinClause()); } return joins; } _isJoinKeyword() { return (this.curTokenIs(TokenType.INNER) || this.curTokenIs(TokenType.LEFT) || this.curTokenIs(TokenType.RIGHT) || this.curTokenIs(TokenType.CROSS) || this.curTokenIs(TokenType.JOIN)); } /** 解析单个 JOIN 子句 */ parseJoinClause() { let type = 'INNER'; if (this.curTokenIs(TokenType.INNER)) { type = 'INNER'; this.nextToken(); } else if (this.curTokenIs(TokenType.LEFT)) { type = 'LEFT'; this.nextToken(); if (this.curTokenIs(TokenType.OUTER)) this.nextToken(); // 可选 OUTER } else if (this.curTokenIs(TokenType.RIGHT)) { type = 'RIGHT'; this.nextToken(); if (this.curTokenIs(TokenType.OUTER)) this.nextToken(); } else if (this.curTokenIs(TokenType.CROSS)) { type = 'CROSS'; this.nextToken(); } this.expect(TokenType.JOIN); const tableName = this.expectIdentifier('table name'); // JOIN 表别名(可选) let alias; if (this.curTokenIs(TokenType.AS)) { this.nextToken(); alias = this.expectIdentifier('alias'); } else if (this.curToken.type === TokenType.IDENTIFIER && !this._isJoinReserved()) { alias = this.curToken.value; this.nextToken(); } // ON 条件(CROSS JOIN 不需要 ON) let on = {}; if (type !== 'CROSS' && this.curTokenIs(TokenType.ON)) { this.nextToken(); on = this.parseCondition(); } return { type, table: tableName, alias, on }; } /** 判断当前 token 是否为 FROM 之后的保留字 */ _isReservedAfterFrom() { return (this.curTokenIs(TokenType.WHERE) || this.curTokenIs(TokenType.ORDER) || this.curTokenIs(TokenType.LIMIT) || this.curTokenIs(TokenType.OFFSET) || this.curTokenIs(TokenType.GROUP) || this._isJoinKeyword()); } _isJoinReserved() { return (this.curTokenIs(TokenType.ON) || this.curTokenIs(TokenType.WHERE) || this.curTokenIs(TokenType.ORDER) || this.curTokenIs(TokenType.LIMIT) || this._isJoinKeyword()); } // =================================================================== // INSERT // =================================================================== parseInsert() { this.expect(TokenType.INSERT); this.expect(TokenType.INTO); const tableName = this.expectIdentifier('table name'); // 列名(可选) let columns; if (this.curTokenIs(TokenType.LPAREN)) { this.nextToken(); columns = this.parseIdentifierList(); this.expect(TokenType.RPAREN); } // VALUES this.expect(TokenType.VALUES); // 值列表 const values = []; do { if (this.curTokenIs(TokenType.COMMA)) { this.nextToken(); } this.expect(TokenType.LPAREN); const rowValues = this.parseValueList(); this.expect(TokenType.RPAREN); values.push(rowValues); } while (this.curTokenIs(TokenType.COMMA)); return { type: 'INSERT', into: tableName, columns, values, }; } // =================================================================== // UPDATE // =================================================================== parseUpdate() { this.expect(TokenType.UPDATE); const tableName = this.expectIdentifier('table name'); this.expect(TokenType.SET); // SET col=val, ... const sets = {}; do { if (this.curTokenIs(TokenType.COMMA)) this.nextToken(); const col = this.expectIdentifier('column name'); this.expect(TokenType.EQ); sets[col] = this.parseValue(); } while (this.curTokenIs(TokenType.COMMA)); let where = {}; if (this.curTokenIs(TokenType.WHERE)) { this.nextToken(); where = this.parseCondition(); } return { type: 'UPDATE', table: tableName, sets, where }; } // =================================================================== // DELETE // =================================================================== parseDelete() { this.expect(TokenType.DELETE); this.expect(TokenType.FROM); const tableName = this.expectIdentifier('table name'); let where = {}; if (this.curTokenIs(TokenType.WHERE)) { this.nextToken(); where = this.parseCondition(); } return { type: 'DELETE', from: tableName, where }; } // =================================================================== // CREATE TABLE // =================================================================== parseCreateTable() { this.expect(TokenType.CREATE); this.expect(TokenType.TABLE); const tableName = this.expectIdentifier('table name'); this.expect(TokenType.LPAREN); const columns = []; do { if (this.curTokenIs(TokenType.COMMA)) this.nextToken(); columns.push(this.parseColumnDef()); } while (this.curTokenIs(TokenType.COMMA)); this.expect(TokenType.RPAREN); return { type: 'CREATE_TABLE', name: tableName, columns }; } parseColumnDef() { const name = this.expectIdentifier('column name'); const type = this.expectIdentifier('column type').toLowerCase(); const col = { name, type }; // 修饰符 while (this.curTokenIs(TokenType.PRIMARY) || this.curTokenIs(TokenType.UNIQUE) || this.curTokenIs(TokenType.NOT) || this.curTokenIs(TokenType.DEFAULT)) { if (this.curTokenIs(TokenType.PRIMARY)) { this.nextToken(); this.expect(TokenType.KEY); col.primaryKey = true; } else if (this.curTokenIs(TokenType.UNIQUE)) { this.nextToken(); col.unique = true; } else if (this.curTokenIs(TokenType.NOT)) { this.nextToken(); // 支持 NOT NULL → required this.expect(TokenType.NULL); col.required = true; } else if (this.curTokenIs(TokenType.DEFAULT)) { this.nextToken(); col.default = this.parseValue(); } else { break; } } return col; } // =================================================================== // DROP TABLE // =================================================================== parseDropTable() { this.expect(TokenType.DROP); this.expect(TokenType.TABLE); const tableName = this.expectIdentifier('table name'); return { type: 'DROP_TABLE', name: tableName }; } // =================================================================== // 条件表达式 // =================================================================== /** condition → simple_cond ((AND|OR) simple_cond)* */ parseCondition() { let left = this.parseSimpleCondition(); while (this.curTokenIs(TokenType.AND) || this.curTokenIs(TokenType.OR)) { const isAnd = this.curTokenIs(TokenType.AND); this.nextToken(); const right = this.parseSimpleCondition(); if (isAnd) { // 合并到 $and left = { $and: [left, right] }; } else { left = { $or: [left, right] }; } } return left; } /** simple_cond → column op value | column IS [NOT] NULL | column [NOT] LIKE pattern * | column [NOT] IN (values) | NOT condition | (condition) */ parseSimpleCondition() { // NOT expr if (this.curTokenIs(TokenType.NOT)) { this.nextToken(); const inner = this.parseSimpleCondition(); return { $not: inner }; } // (condition) if (this.curTokenIs(TokenType.LPAREN)) { this.nextToken(); const inner = this.parseCondition(); this.expect(TokenType.RPAREN); return inner; } // column const column = this.parseColumnRef(); // IS NULL / IS NOT NULL if (this.curTokenIs(TokenType.IDENTIFIER) && this.curToken.value.toUpperCase() === 'IS') { this.nextToken(); const isNot = this.curTokenIs(TokenType.NOT); if (isNot) this.nextToken(); this.expect(TokenType.NULL); const result = {}; result[column] = isNot ? { $ne: null } : { $eq: null }; return result; } // LIKE if (this.curTokenIs(TokenType.LIKE)) { this.nextToken(); const pattern = this.parseValue(); const result = {}; result[column] = { $like: pattern }; return result; } // IN if (this.curTokenIs(TokenType.IN)) { this.nextToken(); this.expect(TokenType.LPAREN); const values = this.parseValueList(); this.expect(TokenType.RPAREN); const result = {}; result[column] = { $in: values }; return result; } // 比较运算符 const op = this.parseComparisonOp(); // 尝试解析列引用(identifier DOT identifier 格式) let value; if ((this.curToken.type === TokenType.IDENTIFIER || this._isKeywordAsIdent()) && this.peekTokenIs(TokenType.DOT)) { const colRef = this.parseColumnRef(); value = { $col: colRef }; } else { value = this.parseValue(); } const result = {}; result[column] = { [op]: value }; return result; } peekTokenIs(type) { return this.peekToken.type === type; } parseComparisonOp() { switch (this.curToken.type) { case TokenType.EQ: this.nextToken(); return '$eq'; case TokenType.NEQ: this.nextToken(); return '$ne'; case TokenType.GT: this.nextToken(); return '$gt'; case TokenType.GTE: this.nextToken(); return '$gte'; case TokenType.LT: this.nextToken(); return '$lt'; case TokenType.LTE: this.nextToken(); return '$lte'; default: throw this.error(`Expected comparison operator, got "${this.curToken.value}"`); } } // =================================================================== // 辅助解析 // =================================================================== parseColumnList() { const cols = []; cols.push(this.parseColumnRef()); while (this.curTokenIs(TokenType.COMMA)) { this.nextToken(); cols.push(this.parseColumnRef()); } return cols; } /** 解析列引用:支持 'col'、'table.col' 和 'COUNT(*)'/'SUM(col)' 等 */ parseColumnRef() { // 聚合函数? if (this.curTokenIs(TokenType.COUNT) || this.curTokenIs(TokenType.SUM) || this.curTokenIs(TokenType.AVG) || this.curTokenIs(TokenType.MIN) || this.curTokenIs(TokenType.MAX)) { return this.parseAggregateCall(); } const first = this.expectIdentifier('column name'); if (this.curTokenIs(TokenType.DOT)) { this.nextToken(); const second = this.expectIdentifier('column name'); return `${first}.${second}`; } return first; } /** 解析聚合函数调用: COUNT(*), SUM(col), AVG(col), MIN(col), MAX(col) */ parseAggregateCall() { const func = this.curToken.value.toUpperCase(); this.nextToken(); this.expect(TokenType.LPAREN); let arg; if (this.curTokenIs(TokenType.STAR)) { arg = '*'; this.nextToken(); } else { arg = this.parseColumnRef(); } this.expect(TokenType.RPAREN); // 可选别名: AS alias let alias = ''; if (this.curTokenIs(TokenType.AS)) { this.nextToken(); alias = this.expectIdentifier('alias'); } else if (this.curToken.type === TokenType.IDENTIFIER && this._isAggregateAlias()) { alias = this.curToken.value; this.nextToken(); } if (alias) { return `${func}(${arg}) AS ${alias}`; } return `${func}(${arg})`; } _isAggregateAlias() { return !this._isReservedAfterFrom() && !this._isJoinKeyword(); } parseIdentifierList() { const ids = []; ids.push(this.expectIdentifier('identifier')); while (this.curTokenIs(TokenType.COMMA)) { this.nextToken(); ids.push(this.expectIdentifier('identifier')); } return ids; } parseValueList() { const vals = []; vals.push(this.parseValue()); while (this.curTokenIs(TokenType.COMMA)) { this.nextToken(); vals.push(this.parseValue()); } return vals; } parseOrderByList() { const list = []; list.push(this.parseOrderBy()); while (this.curTokenIs(TokenType.COMMA)) { this.nextToken(); list.push(this.parseOrderBy()); } return list; } parseOrderBy() { const column = this.expectIdentifier('column name'); let direction = 'asc'; if (this.curTokenIs(TokenType.ASC)) { this.nextToken(); } else if (this.curTokenIs(TokenType.DESC)) { direction = 'desc'; this.nextToken(); } return { column, direction }; } /** 解析字面量值 */ parseValue() { switch (this.curToken.type) { case TokenType.STRING: { const val = this.curToken.value; this.nextToken(); return val; } case TokenType.NUMBER: { const val = Number(this.curToken.value); this.nextToken(); return val; } case TokenType.TRUE: this.nextToken(); return true; case TokenType.FALSE: this.nextToken(); return false; case TokenType.NULL: this.nextToken(); return null; default: throw this.error(`Expected value, got "${this.curToken.value}"`); } } // =================================================================== // Token 操作 // =================================================================== nextToken() { this.curToken = this.peekToken; this.peekToken = this.lexer.nextToken(); } curTokenIs(type) { return this.curToken.type === type; } expect(type) { if (this.curTokenIs(type)) { this.nextToken(); return; } throw this.error(`Expected ${type}, got "${this.curToken.value}"`); } expectIdentifier(context) { if (this.curToken.type === TokenType.IDENTIFIER || this._isKeywordAsIdent()) { const val = this.curToken.value; this.nextToken(); return val; } throw this.error(`Expected ${context}, got "${this.curToken.value}"`); } /** 关键字可以作为标识符(如列名等于关键字) */ _isKeywordAsIdent() { return (this.curToken.type !== TokenType.EOF && this.curToken.type !== TokenType.ILLEGAL && this.curToken.type !== TokenType.STRING && this.curToken.type !== TokenType.NUMBER && this.curToken.type !== TokenType.COMMA && this.curToken.type !== TokenType.LPAREN && this.curToken.type !== TokenType.RPAREN && this.curToken.type !== TokenType.SEMICOLON && this.curToken.type !== TokenType.EQ && this.curToken.type !== TokenType.NEQ && this.curToken.type !== TokenType.GT && this.curToken.type !== TokenType.GTE && this.curToken.type !== TokenType.LT && this.curToken.type !== TokenType.LTE && this.curToken.type !== TokenType.DOT && this.curToken.type !== TokenType.STAR); } expectNumber(context) { if (this.curToken.type === TokenType.NUMBER) { const val = Number(this.curToken.value); this.nextToken(); return val; } throw this.error(`Expected ${context}, got "${this.curToken.value}"`); } error(msg) { return new DatabaseError(`Parse error at position ${this.curToken.position}: ${msg}`, 'PARSE_ERROR'); } } // --------------------------------------------------------------------------- // 便捷方法 // --------------------------------------------------------------------------- /** 解析 SQL 字符串为 AST Statement */ function parse(sql) { const parser = new Parser(sql); const stmt = parser.parseStatement(); return stmt; } /** * metona-sqlark Transaction — 事务管理 * @module transaction * * 封装多操作事务,保证原子性。 * v0.0.1: 基于引擎层操作实现,IndexedDB 引擎利用原生事务。 */ // --------------------------------------------------------------------------- // Transaction // --------------------------------------------------------------------------- class Transaction { constructor(engine) { this.tables = new Map(); this.completed = false; this.engine = engine; } /** 获取表操作对象 */ table(tableName) { let t = this.tables.get(tableName); if (!t) { t = new Table(this.engine, tableName); this.tables.set(tableName, t); } return t; } /** 标记事务完成(由 TransactionManager 调用) */ _markCompleted() { this.completed = true; } /** 是否已完成 */ isCompleted() { return this.completed; } } // --------------------------------------------------------------------------- // TransactionManager // --------------------------------------------------------------------------- class TransactionManager { constructor(engine) { this.engine = engine; } /** 执行事务 */ async execute(fn) { const trx = new Transaction(this.engine); try { const result = await fn(trx); trx._markCompleted(); return result; } catch (error) { if (error instanceof DatabaseError) throw error; throw new DatabaseError(`Transaction failed: ${error.message}`, 'TRANSACTION_ERROR', error); } } } /** * metona-sqlark Plugin — 插件系统 * @module plugin * * 管理插件的注册、生命周期和钩子调度。 */ // --------------------------------------------------------------------------- // PluginManager // --------------------------------------------------------------------------- class PluginManager { constructor() { this.plugins = []; this.hooks = new Map(); } /** 注册插件 */ register(plugin) { // 按优先级插入 const priority = plugin.priority ?? 0; const insertIndex = this.plugins.findIndex((p) => (p.priority ?? 0) < priority); if (insertIndex === -1) { this.plugins.push(plugin); } else { this.plugins.splice(insertIndex, 0, plugin); } // 安装 plugin.install(null); // 实际引用由 MetonaSqlark 注入 } /** 卸载插件 */ unregister(pluginName) { const idx = this.plugins.findIndex((p) => p.name === pluginName); if (idx !== -1) { this.plugins[idx].destroy(); this.plugins.splice(idx, 1); } } /** 获取所有已注册插件 */ getPlugins() { return [...this.plugins]; } /** 添加钩子回调 */ on(hook, callback) { const callbacks = this.hooks.get(hook) ?? []; callbacks.push(callback); this.hooks.set(hook, callbacks); } /** 移除钩子回调 */ off(hook, callback) { const callbacks = this.hooks.get(hook); if (callbacks) { const idx = callbacks.indexOf(callback); if (idx !== -1) callbacks.splice(idx, 1); } } /** 触发钩子 */ async trigger(hook, ...args) { const callbacks = this.hooks.get(hook); if (callbacks) { for (const cb of callbacks) { await cb(...args); } } } /** 销毁所有插件 */ destroy() { for (const plugin of this.plugins) { try { plugin.destroy(); } catch (e) { // eslint-disable-next-line no-console console.warn(`[metona-sqlark] Plugin "${plugin.name}" destroy error:`, e); } } this.plugins = []; this.hooks.clear(); } } /** * metona-sqlark Core — 数据库主类 * @module core * * 管理数据库生命周期、引擎调度、表操作、SQL 查询、事务和插件。 */ // --------------------------------------------------------------------------- // MetonaSqlark // --------------------------------------------------------------------------- class MetonaSqlark { /** 获取版本号 */ get version() { return this._version; } constructor(config) { this.ready = false; this.tableCache = new Map(); // ---- 发布订阅 ---- this.listeners = new Map(); // ---- 迁移 ---- this.migrations = new Map(); this.config = config; this.name = config.name ?? DB_DEFAULTS.name; this.mode = config.mode ?? DB_DEFAULTS.mode; this._version = config.version ?? DB_DEFAULTS.version; this.pluginManager = new PluginManager(); } // ---- 初始化 ---- /** 初始化数据库(创建引擎、打开连接) */ async init() { // 创建引擎 this.engine = this.createEngine(); // 打开连接 await this.engine.open(this.name, this.version); // 初始化执行器和事务管理器 this.executor = new QueryExecutor(this.engine); this.transactionManager = new TransactionManager(this.engine); // 注册插件 if (this.config.plugins) { for (const plugin of this.config.plugins) { this.pluginManager.register(plugin); } } this.ready = true; // 回调 if (this.config.onReady) { this.config.onReady(this); } } /** 检查是否就绪 */ isReady() { return this.ready; } // ---- 表管理 ---- /** 创建表 */ async defineTable(name, columns) { this.ensureReady(); const schema = createSchema(name, columns); await this.pluginManager.trigger('beforeCreateTable', schema); await this.engine.createTable(schema); await this.pluginManager.trigger('afterCreateTable', schema); // 清除缓存 this.tableCache.delete(name); } /** 获取表操作对象 */ table(name) { this.ensureReady(); let t = this.tableCache.get(name); if (!t) { t = new Table(this.engine, name, this.executor); this.tableCache.set(name, t); } return t; } /** 删除表 */ async dropTable(name) { this.ensureReady(); await this.pluginManager.trigger('beforeDropTable', name); await this.engine.dropTable(name); await this.pluginManager.trigger('afterDropTable', name); this.tableCache.delete(name); } /** 获取所有表名 */ async getTableNames() { this.ensureReady(); return this.engine.getTableNames(); } // ---- SQL 查询 ---- /** 执行 SQL 字符串查询 */ async query(sql) { this.ensureReady(); await this.pluginManager.trigger('beforeQuery', sql); const stmt = parse(sql); const result = await this.executor.execute(stmt); await this.pluginManager.trigger('afterQuery', sql, result); return result; } // ---- 事务 ---- /** 执行事务 */ async transaction(fn) { this.ensureReady(); await this.pluginManager.trigger('beforeTransaction'); const result = await this.transactionManager.execute(fn); await this.pluginManager.trigger('afterTransaction'); return result; } // ---- 导入导出 ---- /** 导出表数据为 JSON */ async exportTable(tableName) { this.ensureReady(); return this.engine.find(tableName, { table: tableName }); } /** 导入 JSON 数据到表 */ async importTable(tableName, data) { this.ensureReady(); return this.engine.insert(tableName, data); } /** 导出整个数据库为 JSON */ async exportAll() { this.ensureReady(); const result = {}; const names = await this.engine.getTableNames(); for (const name of names) { result[name] = await this.engine.find(name, { table: name }); } return result; } /** 订阅表变更 */ subscribe(tableName, callback) { const key = `change:${tableName}`; if (!this.listeners.has(key)) this.listeners.set(key, new Set()); this.listeners.get(key).add(callback); return () => this.listeners.get(key)?.delete(callback); } /** 触发变更事件 */ emit(tableName, event) { const key = `change:${tableName}`; this.listeners.get(key)?.forEach((cb) => cb(event)); } /** 注册迁移 */ addMigration(version, up) { this.migrations.set(version, up); } /** 执行迁移到指定版本 */ async migrateTo(targetVersion) { this.ensureReady(); for (const [version, up] of [...this.migrations.entries()].sort((a, b) => a[0] - b[0])) { if (version <= targetVersion && version > this.version) { await up(this); this._version = version; } } } // ---- 插件 ---- /** 获取插件管理器 */ getPluginManager() { return this.pluginManager; } /** 注册钩子 */ on(hook, callback) { this.pluginManager.on(hook, callback); } // ---- 生命周期 ---- /** 关闭数据库 */ async close() { this.pluginManager.destroy(); await this.engine.close(); this.tableCache.clear(); this.ready = false; } /** 获取底层引擎 */ getEngine() { return this.engine; } // ---- 内部 ---- createEngine() { const mode = this.mode; const diskEngine = this.config.diskEngine ?? 'indexeddb'; switch (mode) { case 'memory': return new MemoryEngine(); case 'disk': return diskEngine === 'opfs' ? new OPFSEngine() : new IndexedDBEngine(); case 'hybrid': return new HybridEngine(diskEngine); default: throw new DatabaseError(`Unknown storage mode: ${mode}`, 'CONFIG_ERROR'); } } ensureReady() { if (!this.ready) { throw new DatabaseError('Database not initialized. Call await db.init() first.', 'DB_NOT_READY'); } } } /** * metona-sqlark — 入口文件 * @module metona-sqlark * @version 0.1.12 * * 前端关系型数据库,内存与磁盘双模式。 * 支持 Query Builder 链式 API 和 SQL 字符串查询。 */ // --------------------------------------------------------------------------- // 工厂函数 // --------------------------------------------------------------------------- /** * 创建数据库实例并初始化 * * @example * ```ts * const db = await MetonaSqlark.create({ * name: 'my-app', * mode: 'hybrid', * }); * * await db.defineTable('users', { * id: { type: 'string', primaryKey: true }, * name: { type: 'string', required: true }, * }); * * await db.table('users').insert({ id: '1', name: 'Alice' }); * const results = await db.query('SELECT * FROM users'); * ``` */ async function create(config) { const db = new MetonaSqlark(config); await db.init(); return db; } // --------------------------------------------------------------------------- // 全局 API // --------------------------------------------------------------------------- const api = { VERSION, version: VERSION, create, MetonaSqlark, MeSqlark: MetonaSqlark, }; if (typeof window !== 'undefined') { window.MetonaSqlark = api; window.MeSqlark = api; } // 别名 const MeSqlark = MetonaSqlark; exports.HybridEngine = HybridEngine; exports.IndexedDBEngine = IndexedDBEngine; exports.MeSqlark = MeSqlark; exports.MemoryEngine = MemoryEngine; exports.MetonaSqlark = MetonaSqlark; exports.OPFSEngine = OPFSEngine; exports.Table = Table; exports.VERSION = VERSION; exports.api = api; exports.create = create; exports.default = api; exports.parse = parse; exports.tokenize = tokenize; Object.defineProperty(exports, '__esModule', { value: true }); })); //# sourceMappingURL=metona-sqlark.js.map