Skip to content

Commit e82f868

Browse files
committed
fix: looseEqual(null, null) returned true, CTE inlining missed distinct/windows
Two SQL correctness bugs: 1. looseEqual() fell through to `return a === b` for nulls, so null===null was true. This broke simple CASE: `CASE NULL WHEN NULL THEN 'hit' END` incorrectly matched. Added null/undefined guard at top of looseEqual — NULL is never equal to anything per SQL. 2. CTE inlining guard didn't check for distinct or window functions. A CTE like `WITH t AS (SELECT DISTINCT ...)` would be inlined, silently dropping the DISTINCT/window semantics. Added guards.
1 parent 1a5edc2 commit e82f868

File tree

2 files changed

+6
-2
lines changed

2 files changed

+6
-2
lines changed

src/sql/compiler.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,12 @@ export function compileFull(stmt: SelectStmt): SqlCompileResult {
182182
const cte = cteMap.get(desc.table);
183183
if (cte) {
184184
const base = cte.descriptor;
185-
// Guard: CTEs with sort/limit/aggregates/groupBy/having can't be inlined —
186-
// they change the result shape and need materialization first.
185+
// Guard: CTEs with sort/limit/aggregates/groupBy/having/distinct/windows
186+
// can't be inlined — they change the result shape and need materialization first.
187187
const canInline = !base.sortColumn && base.limit === undefined &&
188188
!base.aggregates && !base.groupBy && !cte.havingExpr && !cte.allOrderBy &&
189+
!(base.distinct && base.distinct.length > 0) &&
190+
!(base.windows && base.windows.length > 0) &&
189191
!(base.filterGroups && base.filterGroups.length > 0 && desc.filterGroups && desc.filterGroups.length > 0);
190192
if (canInline) {
191193
desc.table = base.table;

src/sql/evaluator.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ function evaluateUnary(op: string, operandExpr: SqlExpr, row: Row): unknown {
152152
}
153153

154154
function looseEqual(a: unknown, b: unknown): boolean {
155+
// SQL semantics: NULL is never equal to anything (including NULL)
156+
if (a === null || a === undefined || b === null || b === undefined) return false;
155157
if (typeof a === "number" && typeof b === "number") return a === b;
156158
if (typeof a === "bigint" && typeof b === "bigint") return a === b;
157159
if (typeof a === "string" && typeof b === "string") return a === b;

0 commit comments

Comments
 (0)