test: v0.1.13 新增42项测试覆盖(事务回滚/子查询/外键级联/连接池)
CI / test (18.x) (push) Successful in 9m52s
CI / test (20.x) (push) Successful in 9m50s
CI / test (22.x) (push) Successful in 9m51s
CI / test (24.x) (push) Successful in 9m49s

This commit is contained in:
thzxx
2026-07-26 16:40:11 +08:00
parent b6d814aefe
commit 71e238a5cc
14 changed files with 955 additions and 16 deletions
+67 -3
View File
@@ -1408,14 +1408,19 @@ class QueryExecutor {
stmt.where = await this.resolveSubqueries(stmt.where);
}
const hasGroupBy = !!(stmt.groupBy && stmt.groupBy.length > 0);
const hasAggregate = !hasGroupBy && this._hasAggregateColumn(stmt.columns);
let rows;
if (!stmt.joins || stmt.joins.length === 0) {
const plan = compileStatement(hasGroupBy ? { ...stmt, columns: ['*'] } : stmt);
const plan = compileStatement(hasGroupBy || hasAggregate ? { ...stmt, columns: ['*'] } : stmt);
rows = await this.engine.find(plan.table, plan);
}
else {
rows = await this.executeJoinSelect(stmt);
}
// 无 GROUP BY 但有聚合 → 计算单行聚合结果
if (hasAggregate) {
rows = [this.computeSingleAggregate(rows, stmt)];
}
if (hasGroupBy)
rows = this.executeGroupBy(rows, stmt);
if (stmt.distinct)
@@ -1588,6 +1593,31 @@ class QueryExecutor {
}
getEngine() { return this.engine; }
// ===================================================================
// 无 GROUP BY 时的聚合计算
// ===================================================================
/** 检查 SELECT 列列表中是否包含聚合函数 */
_hasAggregateColumn(columns) {
return columns.some((col) => /^(COUNT|SUM|AVG|MIN|MAX)\(/i.test(col));
}
/** 计算单行聚合结果(无 GROUP BY) */
computeSingleAggregate(rows, stmt) {
const result = {};
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;
result[alias || colExpr] = this.computeAggregate(func.toUpperCase(), rows, arg.trim());
}
else {
// 非聚合列取第一行的值
result[colExpr] = rows.length > 0 ? rows[0][colExpr] : null;
}
}
return result;
}
// ===================================================================
// 子查询解析
// ===================================================================
/**
@@ -2394,8 +2424,8 @@ class Parser {
/** 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)) {
// NOT expr(注意 NOT IN / NOT LIKE 不作为通用 NOT
if (this.curTokenIs(TokenType.NOT) && !this._isNotInOrLike()) {
this.nextToken();
const inner = this.parseSimpleCondition();
return { $not: inner };
@@ -2420,6 +2450,36 @@ class Parser {
result[column] = isNot ? { $ne: null } : { $eq: null };
return result;
}
// NOT LIKE / NOT INNOT 后紧跟 LIKE 或 IN
if (this.curTokenIs(TokenType.NOT)) {
if (this.peekTokenIs(TokenType.IN)) {
// NOT IN
this.nextToken(); // skip NOT
this.nextToken(); // skip IN
this.expect(TokenType.LPAREN);
if (this.curTokenIs(TokenType.SELECT)) {
const subquery = this.parseSelect();
this.expect(TokenType.RPAREN);
const result = {};
result[column] = { $nin: { $subquery: subquery } };
return result;
}
const values = this.parseValueList();
this.expect(TokenType.RPAREN);
const result = {};
result[column] = { $nin: values };
return result;
}
else if (this.peekTokenIs(TokenType.LIKE)) {
// NOT LIKE
this.nextToken(); // skip NOT
this.nextToken(); // skip LIKE
const pattern = this.parseValue();
const result = {};
result[column] = { $not: { $like: pattern } };
return result;
}
}
// LIKE
if (this.curTokenIs(TokenType.LIKE)) {
this.nextToken();
@@ -2471,6 +2531,10 @@ class Parser {
result[column] = { [op]: value };
return result;
}
/** 判断当前 NOT 是否为 NOT IN / NOT LIKE 的一部分(不应作为通用 NOT 处理) */
_isNotInOrLike() {
return this.peekTokenIs(TokenType.IN) || this.peekTokenIs(TokenType.LIKE);
}
peekTokenIs(type) {
return this.peekToken.type === type;
}