Skip to content

Commit 6cd0e4b

Browse files
committed
feat: add NOT LIKE page skip for uniform pages
When a page is uniform (min === max) and the single value matches the LIKE pattern, NOT LIKE would exclude all rows — skip the entire page. Completes coverage of all 14 filter ops for page-level pruning.
1 parent cb2a6f9 commit 6cd0e4b

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

src/decode.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,29 @@ describe("canSkipPage", () => {
218218
const strPage: PageInfo = { byteOffset: 0n, byteLength: 100, rowCount: 50, nullCount: 0, minValue: "apple", maxValue: "banana" };
219219
expect(canSkipPage(strPage, [{ column: "x", op: "like", value: "%xyz%" }], "x")).toBe(false);
220220
});
221+
222+
// NOT LIKE page skip — uniform pages only
223+
it("skips uniform page with NOT LIKE when the single value matches the pattern", () => {
224+
const uniformPage: PageInfo = { byteOffset: 0n, byteLength: 100, rowCount: 50, nullCount: 0, minValue: "hello", maxValue: "hello" };
225+
// "hello" matches "hel%" → NOT LIKE excludes all rows → skip
226+
expect(canSkipPage(uniformPage, [{ column: "x", op: "not_like", value: "hel%" }], "x")).toBe(true);
227+
// "hello" matches "hello" exactly → NOT LIKE excludes all rows → skip
228+
expect(canSkipPage(uniformPage, [{ column: "x", op: "not_like", value: "hello" }], "x")).toBe(true);
229+
// "hello" matches "h_llo" (single-char wildcard) → skip
230+
expect(canSkipPage(uniformPage, [{ column: "x", op: "not_like", value: "h_llo" }], "x")).toBe(true);
231+
});
232+
233+
it("does not skip uniform page with NOT LIKE when the value doesn't match", () => {
234+
const uniformPage: PageInfo = { byteOffset: 0n, byteLength: 100, rowCount: 50, nullCount: 0, minValue: "hello", maxValue: "hello" };
235+
// "hello" does not match "world%" → NOT LIKE keeps all rows → don't skip
236+
expect(canSkipPage(uniformPage, [{ column: "x", op: "not_like", value: "world%" }], "x")).toBe(false);
237+
});
238+
239+
it("does not skip non-uniform page with NOT LIKE", () => {
240+
const mixedPage: PageInfo = { byteOffset: 0n, byteLength: 100, rowCount: 50, nullCount: 0, minValue: "apple", maxValue: "banana" };
241+
// Non-uniform page — can't determine if all values match, so don't skip
242+
expect(canSkipPage(mixedPage, [{ column: "x", op: "not_like", value: "app%" }], "x")).toBe(false);
243+
});
221244
});
222245

223246
describe("assembleRows", () => {

src/decode.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ export function canSkipPage(page: PageInfo, filters: QueryDescriptor["filters"],
8181
if ((max as string) < prefix || (min as string) >= prefixEnd) return true;
8282
break;
8383
}
84+
// not_like: skip uniform pages where the single value matches the pattern
85+
case "not_like": {
86+
if (typeof filter.value !== "string" || typeof min !== "string" || min !== max) break;
87+
if (compileLikeRegex(filter.value).test(min)) return true;
88+
break;
89+
}
8490
}
8591
}
8692
return false;

0 commit comments

Comments
 (0)