From 83c5aa0b9d8d8183f93f3fdab12a77600de36355 Mon Sep 17 00:00:00 2001 From: thzxx Date: Mon, 14 Sep 2026 21:03:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(A11):=20AND=20=E4=BC=98=E5=85=88=E7=BA=A7?= =?UTF-8?q?=E9=AB=98=E4=BA=8E=20OR=EF=BC=88SQL=20=E6=A0=87=E5=87=86?= =?UTF-8?q?=EF=BC=89=E2=80=94=E2=80=94=20parser=20=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F=E4=B8=89=E5=B1=82=E5=88=86=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此前 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 用例通过。 --- src/sql/parser.ts | 50 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/sql/parser.ts b/src/sql/parser.ts index 32593d9..237430e 100644 --- a/src/sql/parser.ts +++ b/src/sql/parser.ts @@ -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 { - let left = this.parseSimpleCondition(); + return this.parseOrExpression(); + } - while (this.curTokenIs(TokenType.AND) || this.curTokenIs(TokenType.OR)) { - const isAnd = this.curTokenIs(TokenType.AND); + /** or_expr → and_expr (OR and_expr)* */ + private parseOrExpression(): WhereCondition { + const operands: WhereCondition[] = [this.parseAndExpression()]; + while (this.curTokenIs(TokenType.OR)) { this.nextToken(); - const right = this.parseSimpleCondition(); - - if (isAnd) { - // 合并到 $and - left = { $and: [left, right] } as unknown as WhereCondition; - } else { - left = { $or: [left, right] } as unknown as WhereCondition; - } + operands.push(this.parseAndExpression()); } + 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) */