release: v0.2.4 — 701 tests, 32 suites, 零死代码, 零空壳, 全模块接入
CI / test (18.x) (push) Successful in 10m0s
CI / test (20.x) (push) Successful in 10m0s
CI / test (22.x) (push) Successful in 10m0s
CI / test (24.x) (push) Successful in 9m54s

This commit is contained in:
thzxx
2026-07-27 22:18:08 +08:00
parent d799e968ab
commit 4c26882caa
16 changed files with 1538 additions and 257 deletions
+14 -13
View File
@@ -9,6 +9,7 @@ import type { IStorageEngine } from '../interface';
import type { QueryPlan, TableSchema, ColumnDef } from '../../constants';
import { DatabaseError } from '../../constants';
import { matchWhere, applyOrderBy, projectColumns } from '../../query/where-matcher';
import { checkFieldType } from '../../table/schema';
import type { AriaEngineConfig, SSTableMeta } from './types';
import { DEFAULT_ARIA_CONFIG } from './types';
@@ -460,6 +461,7 @@ export class AriaEngine implements IStorageEngine {
async count(tableName: string, query?: QueryPlan): Promise<number> {
this.ensureOpen();
this.ensureTable(tableName);
const rows = this.getAllRows(tableName);
if (!query?.where || Object.keys(query.where).length === 0) return rows.length;
return rows.filter((row) => matchWhere(row, query.where!)).length;
@@ -609,22 +611,15 @@ export class AriaEngine implements IStorageEngine {
throw new DatabaseError(`Column "${colName}" is required in table "${schema.name}"`, 'VALIDATION_ERROR');
}
if (value !== undefined && value !== null) {
this.checkType(colName, colDef.type, value);
this.checkType(colName, colDef.type, value, colDef);
}
if (value !== undefined) validated[colName] = value;
}
return validated;
}
private checkType(colName: string, type: string, value: unknown): void {
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 as string))) 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;
}
private checkType(colName: string, type: string, value: unknown, colDef?: ColumnDef): void {
checkFieldType('', colName, type as any, value, colDef);
}
// =======================================================================
@@ -873,8 +868,12 @@ export class AriaEngine implements IStorageEngine {
}
// $gt / $gte / $lt / $lte → 范围扫描
if ('$gt' in c || '$gte' in c || '$lt' in c || '$lte' in c) {
const startKey = c.$gt ? `${String(Number(c.$gt) + 1)}:` : c.$gte ? `${String(c.$gte)}:` : `${col}:`;
const endKey = c.$lt ? `${String(Number(c.$lt) - 1)}:\uffff` : c.$lte ? `${String(c.$lte)}:\uffff` : `${col}:\uffff`;
let startKey = '';
let endKey = '\uffff';
if (c.$gt !== undefined) startKey = `${String(Number(c.$gt) + 1)}:`;
else if (c.$gte !== undefined) startKey = `${String(c.$gte)}:`;
if (c.$lt !== undefined) endKey = `${String(Number(c.$lt) - 1)}:\uffff`;
else if (c.$lte !== undefined) endKey = `${String(c.$lte)}:\uffff`;
return this.indexScanToRows(tableName, pkCol, idxLsm, col, startKey, endKey);
}
}
@@ -887,7 +886,9 @@ export class AriaEngine implements IStorageEngine {
tableName: string, pkCol: string, idxLsm: LSM,
_col: string, startKey: string, endKey: string,
): Record<string, unknown>[] {
const entries = idxLsm.rangeScan(startKey, endKey);
// 使用前缀扫描:endKey 需要包含 \uffff 以匹配所有带后缀的 key
const actualEndKey = endKey.includes('\uffff') ? endKey : `${endKey}\uffff`;
const entries = idxLsm.rangeScan(startKey, actualEndKey);
const rows: Record<string, unknown>[] = [];
for (const [, idxEntry] of entries) {
const pk = (idxEntry as any).pk as string;