release: v0.4.1 — Aria 级联/ALTER/clearAll + 流式查询/派生表 + 正确性加固
新增: - AriaEngine 外键级联(CASCADE/SET NULL/RESTRICT)+ clearAll() 重置 API - 引擎级 alterTable:Aria DROP COLUMN 重写存储行 + schema 持久化 - 流式查询 queryStream / findStream(LSM 惰性扫描不物化) - FROM 派生表 / 多列 ON 哈希连接 / COUNT(DISTINCT) / NULLS FIRST/LAST - 普通列别名 + ORDER BY 别名 + 无表查询 + 字符串常量列 - 演示页引擎切换器(Memory/Aria)+ 预设自动重置 修复: - Aria WAL DROP_TABLE 崩溃恢复(删表复活)+ 恢复后 WAL 截断 - Memory update/delete 索引维护(unique 约束绕过) - 关联 EXISTS 绑定失效 / HAVING 标量子查询 / INSERT SELECT 位置错位 - 裸布尔列条件(WHERE done / CASE WHEN done) - Aria $in 重复行 / JOIN 主表 WHERE 下推 / DROP INDEX 报错 - ORDER BY/GROUP BY/SELECT 表前缀列 + SQL '' 标准转义 质量:894 测试 · 47 套件 · 81.5% 覆盖率
This commit is contained in:
Vendored
+101
-16
@@ -83,6 +83,8 @@ interface OrderBy {
|
||||
column: string;
|
||||
/** 排序方向 */
|
||||
direction: SortDirection;
|
||||
/** v0.4.0: NULL 值排序位置(first 排最前 / last 排最后,默认同引擎行为) */
|
||||
nulls?: 'first' | 'last';
|
||||
}
|
||||
/** 查询计划 — 由 Executor 编译 AST 后生成 */
|
||||
interface QueryPlan {
|
||||
@@ -116,7 +118,7 @@ interface MetonaPlugin {
|
||||
/** 销毁 */
|
||||
destroy(): void;
|
||||
}
|
||||
declare const VERSION = "0.3.2";
|
||||
declare const VERSION = "0.4.1";
|
||||
|
||||
/**
|
||||
* metona-sqlark Plugin — 插件系统
|
||||
@@ -145,11 +147,6 @@ declare class PluginManager {
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* metona-sqlark Engine Interface — 存储引擎抽象接口
|
||||
* @module engine/interface
|
||||
*/
|
||||
|
||||
interface IStorageEngine {
|
||||
/** 引擎名称 */
|
||||
readonly name: string;
|
||||
@@ -173,6 +170,8 @@ interface IStorageEngine {
|
||||
insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]>;
|
||||
/** 查询行 */
|
||||
find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]>;
|
||||
/** v0.4.0: 流式查询 — 逐行回调扫描(有 where/limit/projection,无 orderBy 语义;有 orderBy 时实现可回退物化) */
|
||||
findStream?(tableName: string, query: QueryPlan, onRow: (row: Record<string, unknown>) => void): Promise<number>;
|
||||
/** 更新行,返回影响行数 */
|
||||
update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number>;
|
||||
/** 删除行,返回影响行数 */
|
||||
@@ -181,6 +180,10 @@ interface IStorageEngine {
|
||||
count(tableName: string, query?: QueryPlan): Promise<number>;
|
||||
/** 清空表数据(保留结构) */
|
||||
clear(tableName: string): Promise<void>;
|
||||
/** v0.4.1: ALTER TABLE(可选)— 引擎级结构变更(Aria 需重写存储行,其余引擎走 Executor 通用路径) */
|
||||
alterTable?(tableName: string, action: 'ADD' | 'DROP', column: ColumnDef & {
|
||||
name: string;
|
||||
}): Promise<void>;
|
||||
/** 创建二级索引(CREATE INDEX) */
|
||||
createIndex?(tableName: string, column: string, unique?: boolean): Promise<void>;
|
||||
/** 删除二级索引(DROP INDEX) */
|
||||
@@ -281,6 +284,8 @@ interface SelectStatement {
|
||||
columns: ColumnRef[];
|
||||
distinct?: boolean;
|
||||
from: string;
|
||||
/** v0.4.0: FROM (SELECT ...) 派生表(存在时 from 为占位,行源取此子查询结果) */
|
||||
fromSubquery?: SelectStatement | SelectUnionStatement;
|
||||
/** 主表别名 */
|
||||
alias?: string;
|
||||
/** JOIN 子句列表 */
|
||||
@@ -364,7 +369,13 @@ declare class QueryExecutor {
|
||||
private executeJoinSelect;
|
||||
private prefixRow;
|
||||
/**
|
||||
* 哈希连接(v0.3.2):ON 为单一等值条件且右表列为索引/主键时,
|
||||
* v0.4.1: 提取可下推的 WHERE 条件 — 主表别名前缀的普通条件(如 o.user_id = '1')。
|
||||
* 下推到引擎可走二级索引;$col/$subquery/$and/$or/$not 等复杂条件保守不下推。
|
||||
*/
|
||||
private extractPushableWhere;
|
||||
/**
|
||||
* 哈希连接(v0.3.2 单等值 / v0.4.0 多列等值):
|
||||
* ON 为等值条件(单列或多列)且右表任一列为索引/主键时,
|
||||
* 收集左表连接值 → 一次 $in 查询右表 → 哈希映射匹配。
|
||||
* 替代嵌套循环,大表 INNER/LEFT JOIN 复杂度 O(N + M)。
|
||||
* 不适用时返回 null(回退嵌套循环)。
|
||||
@@ -389,11 +400,17 @@ declare class QueryExecutor {
|
||||
private executeRollback;
|
||||
/** 列列表是否包含 CASE WHEN 表达式 */
|
||||
private hasCaseColumn;
|
||||
/**
|
||||
* v0.3.3: ORDER BY 是否引用 SELECT 别名(如 `SELECT name AS n ... ORDER BY n`)。
|
||||
* 别名列在引擎层投影前不存在,需投影后重新排序。
|
||||
*/
|
||||
private orderByUsesSelectAlias;
|
||||
/** WHERE 是否包含 CASE WHEN 表达式键 */
|
||||
private whereHasCase;
|
||||
getEngine(): IStorageEngine;
|
||||
/**
|
||||
* 列投影(v0.3.1):普通列走 projectColumns,CASE WHEN 表达式逐行求值
|
||||
* 列投影(v0.3.1):普通列走 projectColumns,CASE WHEN 表达式逐行求值;
|
||||
* v0.3.3: 支持 `col AS alias` 列别名
|
||||
*/
|
||||
private projectRow;
|
||||
/** 检查 SELECT 列列表中是否包含聚合函数 */
|
||||
@@ -516,6 +533,13 @@ declare class Table<T = Record<string, unknown>> {
|
||||
insert(row: T & Record<string, unknown>): Promise<string>;
|
||||
insertMany(rows: (T & Record<string, unknown>)[]): Promise<string[]>;
|
||||
select(columns?: string[]): SelectQueryBuilder;
|
||||
/** v0.4.0: 流式查询 — 逐行回调,不物化全部结果 */
|
||||
stream(onRow: (row: T & Record<string, unknown>) => void, query?: {
|
||||
where?: Record<string, unknown>;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
columns?: string[];
|
||||
}): Promise<number>;
|
||||
update(updates: Partial<T> & Record<string, unknown>): UpdateQueryBuilder;
|
||||
delete(): DeleteQueryBuilder;
|
||||
count(where?: Record<string, unknown>): Promise<number>;
|
||||
@@ -580,6 +604,23 @@ declare class MetonaSqlark {
|
||||
getTableNames(): Promise<string[]>;
|
||||
/** 执行 SQL 字符串查询 */
|
||||
query(sql: string): Promise<unknown>;
|
||||
/**
|
||||
* 流式查询:逐行回调,不一次性物化全部结果(大表友好)。
|
||||
* 支持简单 SELECT(WHERE/LIMIT/OFFSET/列投影);
|
||||
* JOIN/GROUP BY/UNION/聚合/ORDER BY 自动回退为物化查询后逐行回调。
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* let total = 0;
|
||||
* await db.queryStream('SELECT * FROM logs WHERE level = \'error\'', (row) => {
|
||||
* total++;
|
||||
* processRow(row);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
queryStream<T extends Record<string, unknown> = Record<string, unknown>>(sql: string, onRow: (row: T) => void): Promise<number>;
|
||||
/** 流式查询用:剥离主表别名前缀(复用 query 路径的规范化逻辑) */
|
||||
private normalizeWhereForStream;
|
||||
/** 执行事务 */
|
||||
transaction<T>(fn: (trx: Transaction) => Promise<T>): Promise<T>;
|
||||
/** 导出表数据为 JSON */
|
||||
@@ -648,6 +689,8 @@ declare class MemoryEngine implements IStorageEngine {
|
||||
getTableSchema(tableName: string): Promise<TableSchema | null>;
|
||||
insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]>;
|
||||
find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]>;
|
||||
/** v0.4.0: 流式查询 — 逐行回调(单次迭代,不物化结果数组) */
|
||||
findStream(tableName: string, query: QueryPlan, onRow: (row: Record<string, unknown>) => void): Promise<number>;
|
||||
update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number>;
|
||||
delete(tableName: string, query: QueryPlan): Promise<number>;
|
||||
count(tableName: string, query?: QueryPlan): Promise<number>;
|
||||
@@ -669,6 +712,8 @@ declare class MemoryEngine implements IStorageEngine {
|
||||
private tryIndexLookup;
|
||||
/** 更新索引 */
|
||||
private updateIndexes;
|
||||
/** v0.3.3: 从所有索引中移除一行的条目(update/delete 前调用,修复索引过期/残留) */
|
||||
private removeIndexEntries;
|
||||
/**
|
||||
* 级联删除:查找引用 tableName.pkValue 的所有表的行并删除。
|
||||
* @returns 级联删除的行数
|
||||
@@ -706,6 +751,8 @@ declare class IndexedDBEngine implements IStorageEngine {
|
||||
getTableSchema(tableName: string): Promise<TableSchema | null>;
|
||||
insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]>;
|
||||
find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]>;
|
||||
/** v0.4.0: 流式查询 — IDB 批量读入后逐行回调(保持接口一致性) */
|
||||
findStream(tableName: string, query: QueryPlan, onRow: (row: Record<string, unknown>) => void): Promise<number>;
|
||||
update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number>;
|
||||
delete(tableName: string, query: QueryPlan): Promise<number>;
|
||||
count(tableName: string, query?: QueryPlan): Promise<number>;
|
||||
@@ -753,6 +800,8 @@ declare class OPFSEngine implements IStorageEngine {
|
||||
getTableSchema(tableName: string): Promise<TableSchema | null>;
|
||||
insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]>;
|
||||
find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]>;
|
||||
/** v0.4.0: 流式查询(委托内存缓存) */
|
||||
findStream(tableName: string, query: QueryPlan, onRow: (row: Record<string, unknown>) => void): Promise<number>;
|
||||
update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number>;
|
||||
delete(tableName: string, query: QueryPlan): Promise<number>;
|
||||
count(tableName: string, query?: QueryPlan): Promise<number>;
|
||||
@@ -798,13 +847,6 @@ interface AriaEngineConfig {
|
||||
maxMemoryMB?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* AriaEngine — 自研页面式存储引擎主类
|
||||
* @module engine/aria/index
|
||||
*
|
||||
* v0.2.5: WAL 同步修复 + MVCC 接入 + 版本统一 + 生产加固
|
||||
*/
|
||||
|
||||
declare class AriaEngine implements IStorageEngine {
|
||||
readonly name = "aria";
|
||||
private config;
|
||||
@@ -826,6 +868,11 @@ declare class AriaEngine implements IStorageEngine {
|
||||
constructor(config?: AriaEngineConfig);
|
||||
open(dbName: string, _version: number): Promise<void>;
|
||||
close(): Promise<void>;
|
||||
/**
|
||||
* v0.4.1: 重置数据库 — 清空全部数据与表结构(演示页刷新/重新初始化用)。
|
||||
* 清空存储后端、LSM、WAL、MVCC 与二级索引,后续可继续使用本实例。
|
||||
*/
|
||||
clearAll(): Promise<void>;
|
||||
isOpen(): boolean;
|
||||
createTable(schema: TableSchema): Promise<void>;
|
||||
dropTable(tableName: string): Promise<void>;
|
||||
@@ -836,8 +883,32 @@ declare class AriaEngine implements IStorageEngine {
|
||||
find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]>;
|
||||
update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number>;
|
||||
delete(tableName: string, query: QueryPlan): Promise<number>;
|
||||
/**
|
||||
* v0.4.1: 外键级联规则 — 对齐 MemoryEngine.cascadeDelete 行为。
|
||||
* 删除 tableName 主键为 pkValue 的行前,检查引用它的所有表:
|
||||
* - RESTRICT: 存在引用行 → 抛 FOREIGN_KEY_VIOLATION
|
||||
* - CASCADE: 递归删除引用行(含索引/WAL)
|
||||
* - SET NULL: 引用行外键列置 null(含索引/WAL)
|
||||
* @returns 级联影响的行数(CASCADE 删除行数 + SET NULL 更新行数)
|
||||
*/
|
||||
private applyForeignKeyRules;
|
||||
/**
|
||||
* v0.4.0: 流式查询 — 逐行回调,不物化结果数组。
|
||||
* 全表路径走 LSM rangeScanLazy 惰性扫描;索引等值/范围路径复用 tryIndexLookup。
|
||||
* 事务中回退物化(快照合并需要全量行集)。
|
||||
*/
|
||||
findStream(tableName: string, query: QueryPlan, onRow: (row: Record<string, unknown>) => void): Promise<number>;
|
||||
count(tableName: string, query?: QueryPlan): Promise<number>;
|
||||
clear(tableName: string): Promise<void>;
|
||||
/**
|
||||
* v0.4.1: ALTER TABLE — 结构变更真正生效于存储:
|
||||
* - ADD: 持久化 schema(persistSchemas),行无需修改
|
||||
* - DROP: 持久化 schema + 遍历主 LSM 重写所有行(移除该列键)+ WAL UPDATE 记录
|
||||
* (通用路径 getTableSchema 返回副本,Executor 的引用修改对 Aria 无效)
|
||||
*/
|
||||
alterTable(tableName: string, action: 'ADD' | 'DROP', column: ColumnDef & {
|
||||
name: string;
|
||||
}): Promise<void>;
|
||||
createIndex(tableName: string, column: string, unique?: boolean): Promise<void>;
|
||||
dropIndex(tableName: string, column: string, _indexName?: string): Promise<void>;
|
||||
beginTransaction(): Promise<void>;
|
||||
@@ -849,6 +920,11 @@ declare class AriaEngine implements IStorageEngine {
|
||||
releaseSavepoint(name: string): Promise<void>;
|
||||
backup(): Promise<Record<string, Record<string, unknown>[]>>;
|
||||
private getAllRows;
|
||||
/**
|
||||
* v0.3.3: 将事务未提交快照的变更合并到行列表(新增/更新/删除标记)。
|
||||
* 幂等操作:行已是最新时不重复修改。
|
||||
*/
|
||||
private mergeTxnSnapshot;
|
||||
private getPK;
|
||||
private validateRow;
|
||||
private checkType;
|
||||
@@ -864,6 +940,13 @@ declare class AriaEngine implements IStorageEngine {
|
||||
*/
|
||||
private createSSTableStore;
|
||||
private applyWALRecord;
|
||||
/**
|
||||
* v0.3.3: DROP_TABLE 恢复 — 删除 schema 并清除主 LSM 中该表的所有残留数据。
|
||||
*
|
||||
* 此前 DROP_TABLE 在恢复时被忽略,而 CREATE_TABLE 回放会重建 schema,
|
||||
* 导致崩溃后"已删除的表和数据复活"(实证 P0 bug)。
|
||||
*/
|
||||
private applyDropTableRecovery;
|
||||
/** 更新行的二级索引条目 */
|
||||
private updateSecondaryIndexes;
|
||||
/** 通过二级索引快速查找 */
|
||||
@@ -942,6 +1025,8 @@ declare class HybridEngine implements IStorageEngine {
|
||||
getTableSchema(tableName: string): Promise<TableSchema | null>;
|
||||
insert(tableName: string, rows: Record<string, unknown>[]): Promise<string[]>;
|
||||
find(tableName: string, query: QueryPlan): Promise<Record<string, unknown>[]>;
|
||||
/** v0.4.0: 流式查询(内存引擎逐行回调) */
|
||||
findStream(tableName: string, query: QueryPlan, onRow: (row: Record<string, unknown>) => void): Promise<number>;
|
||||
update(tableName: string, query: QueryPlan, updates: Record<string, unknown>): Promise<number>;
|
||||
delete(tableName: string, query: QueryPlan): Promise<number>;
|
||||
count(tableName: string, query?: QueryPlan): Promise<number>;
|
||||
@@ -1131,7 +1216,7 @@ declare class OPFSBackend implements IStorageBackend {
|
||||
/**
|
||||
* metona-sqlark — 入口文件
|
||||
* @module metona-sqlark
|
||||
* @version 0.2.5
|
||||
* @version 0.4.1
|
||||
*
|
||||
* 前端关系型数据库,内存与磁盘双模式。
|
||||
* 支持 Query Builder 链式 API 和 SQL 字符串查询。
|
||||
|
||||
Reference in New Issue
Block a user