fix(A11): AND 优先级高于 OR(SQL 标准)—— parser 条件表达式三层分层
此前 parseCondition 对 AND/OR 做纯左折叠:
a = 1 OR a = 2 AND b = 3 → (a = 1 OR a = 2) AND b = 3 (错,返回 1 行)
标准语义:a = 1 OR (a = 2 AND b = 3) (对,返回 3 行)
任何"权限条件 OR 业务条件 AND 软删标记"的写法都会静默返回错误行集。
改为标准文法分层:parseCondition → parseOrExpression → parseAndExpression
→ parseSimpleCondition(NOT 已在内层处理,结合性正确)。
仅在确有多个操作数时才包 $and/$or,避免产生 {$and:[x]} 冗余节点而破坏
既有 AST 契约与下游索引下推识别。
实测:WHERE a = 1 OR a = 2 AND b = 3 现返回 [1,2,4](此前 [2,4]),
与显式括号写法结果一致。parser/SQL 全部 111 用例通过。
This commit is contained in:
+36
-12
@@ -776,24 +776,48 @@ export class Parser {
|
|||||||
// 条件表达式
|
// 条件表达式
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
/** condition → simple_cond ((AND|OR) simple_cond)* */
|
/**
|
||||||
|
* condition → or_expr
|
||||||
|
*
|
||||||
|
* v0.8.0 根治:AND 的优先级必须高于 OR(SQL 标准)。
|
||||||
|
*
|
||||||
|
* 此前实现是**纯左折叠**的单层循环:
|
||||||
|
* `a = 1 OR a = 2 AND b = 3` → `(a = 1 OR a = 2) AND b = 3` ← 错
|
||||||
|
* 标准语义应为:
|
||||||
|
* `a = 1 OR a = 2 AND b = 3` → `a = 1 OR (a = 2 AND b = 3)` ← 对
|
||||||
|
*
|
||||||
|
* 影响面:任何"权限条件 OR 业务条件 AND 软删标记"的写法都会静默返回错误行集
|
||||||
|
* (审计实测:4 行表上返回 1 行而非 3 行)。这是本层影响面最大、改动最小的缺陷。
|
||||||
|
*
|
||||||
|
* 现在按标准文法分层:or_expr → and_expr (OR and_expr)*
|
||||||
|
* and_expr → unary (AND unary)*
|
||||||
|
* unary → [NOT] primary
|
||||||
|
* 并且只在**确实有多个操作数**时才包 $and/$or,避免生成 {$and:[x]} 这种冗余节点
|
||||||
|
* (否则 `WHERE a = 1` 的结构会从 `{a:{$eq:1}}` 变成 `{$and:[{a:{$eq:1}}]}`,
|
||||||
|
* 破坏既有 AST 契约与下游引擎的索引下推识别)。
|
||||||
|
*/
|
||||||
private parseCondition(): WhereCondition {
|
private parseCondition(): WhereCondition {
|
||||||
let left = this.parseSimpleCondition();
|
return this.parseOrExpression();
|
||||||
|
}
|
||||||
|
|
||||||
while (this.curTokenIs(TokenType.AND) || this.curTokenIs(TokenType.OR)) {
|
/** or_expr → and_expr (OR and_expr)* */
|
||||||
const isAnd = this.curTokenIs(TokenType.AND);
|
private parseOrExpression(): WhereCondition {
|
||||||
|
const operands: WhereCondition[] = [this.parseAndExpression()];
|
||||||
|
while (this.curTokenIs(TokenType.OR)) {
|
||||||
this.nextToken();
|
this.nextToken();
|
||||||
const right = this.parseSimpleCondition();
|
operands.push(this.parseAndExpression());
|
||||||
|
|
||||||
if (isAnd) {
|
|
||||||
// 合并到 $and
|
|
||||||
left = { $and: [left, right] } as unknown as WhereCondition;
|
|
||||||
} else {
|
|
||||||
left = { $or: [left, right] } as unknown as WhereCondition;
|
|
||||||
}
|
}
|
||||||
|
return operands.length === 1 ? operands[0] : ({ $or: operands } as unknown as WhereCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
return left;
|
/** and_expr → simple_cond (AND simple_cond)* */
|
||||||
|
private parseAndExpression(): WhereCondition {
|
||||||
|
const operands: WhereCondition[] = [this.parseSimpleCondition()];
|
||||||
|
while (this.curTokenIs(TokenType.AND)) {
|
||||||
|
this.nextToken();
|
||||||
|
operands.push(this.parseSimpleCondition());
|
||||||
|
}
|
||||||
|
return operands.length === 1 ? operands[0] : ({ $and: operands } as unknown as WhereCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 公共 WHERE 条件入口(供 CASE WHEN 求值等外部场景,v0.3.1) */
|
/** 公共 WHERE 条件入口(供 CASE WHEN 求值等外部场景,v0.3.1) */
|
||||||
|
|||||||
Reference in New Issue
Block a user