From 1b14387aa2572d299be58980fb109f76b027341a Mon Sep 17 00:00:00 2001 From: Zaid Humaimidi Date: Tue, 6 Jan 2026 22:53:30 -0800 Subject: [PATCH] Fix covers() for false and contradictions --- lib/common/expression/synth.ts | 2 +- test/synth.ts | 36 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/common/expression/synth.ts b/lib/common/expression/synth.ts index bd94b09..61edcc6 100644 --- a/lib/common/expression/synth.ts +++ b/lib/common/expression/synth.ts @@ -955,7 +955,7 @@ export function covers(expr1: Expression, expr2: Expression): boolean { expr2 = normalize(expr2); if (!expr2) return true; expr1 = normalize(expr1); - if (!Array.isArray(expr1)) return !!expr1; + if (!Array.isArray(expr1) && expr1) return true; const synt1 = Clause.fromExpression(expr1); const synt2 = Clause.fromExpression(expr2); diff --git a/test/synth.ts b/test/synth.ts index 2506672..fe54a4e 100644 --- a/test/synth.ts +++ b/test/synth.ts @@ -1,7 +1,7 @@ import test from "node:test"; import assert from "node:assert"; import initSqlJs from "sql.js/dist/sql-asm.js"; -import { minimize, unionDiff } from "../lib/common/expression/synth.ts"; +import { covers, minimize, unionDiff } from "../lib/common/expression/synth.ts"; import { parse, stringify } from "../lib/common/expression/parser.ts"; const STRING_VALUES = [null, "", "a", "ab", "ab10", "ab-10"]; @@ -115,3 +115,37 @@ void test("unionDiff", async () => { assert.strictEqual(setsEqual(res4, diffSet), true); } }); + +void test("covers", async () => { + assert.strictEqual(covers(false, false), true); + assert.strictEqual(covers(false, parse("decimal > 5 AND decimal < 3")), true); + assert.strictEqual(covers(parse("true"), parse("decimal > 0")), true); + assert.strictEqual(covers(parse("true"), parse("false")), true); + assert.strictEqual(covers(parse("false"), parse("false")), true); + assert.strictEqual(covers(parse("false"), parse("decimal > 0")), false); + assert.strictEqual(covers(parse("decimal >= 0"), parse("decimal > 0")), true); + assert.strictEqual( + covers(parse("decimal > 0"), parse("decimal >= 0")), + false, + ); + + const cases = [ + ["decimal >= 0", "decimal > 0"], + ["decimal > 0", "decimal > 5"], + ["string IS NOT NULL", "string = 'a'"], + ["true", "decimal > 0"], + ]; + + for (const [c1, c2] of cases) { + const res1 = await query(c1); + const res2 = await query(c2); + const coversResult = covers(parse(c1), parse(c2)); + const actuallyCovers = Array.from(res2).every((r) => res1.has(r)); + + assert.strictEqual( + coversResult, + actuallyCovers, + `covers(${c1}, ${c2}) should match actual coverage`, + ); + } +});