Artifact d5dd8ccde29235b73b2952ee0796829a167d73b670cc5d0e5dd16a070f25bc8f:
- File
internal/index/prefix_test.go
— part of check-in
[22346a81a7]
at
2026-08-31 13:14:29
on branch trunk
— prefix lookups seek instead of scanning; sqlite tuned for this workload
col LIKE ? || '%' cannot use an index: the pattern is not a literal when the statement is prepared, so finding ONE check-in by its full hash read all 36,853 rows of sqlite's tree. That was 10s on a first timeline render and 40ms after, which reads as cold I/O and is not. Half-open hex ranges seek. Also sets cache_size, mmap_size and temp_store, none of which were configured. (user: quarry, size: 2569) [annotate] [blame] [check-ins using]
package index import "testing" // A prefix lookup must be an index SEEK, not a scan. `col LIKE ? || '%'` // cannot use an index — the pattern is not a literal when the statement // is prepared — so finding one check-in by its full hash read all 36,853 // rows of sqlite's tree, comments included. That was ten seconds on the // first timeline render and forty milliseconds on every later one, which // is exactly the shape that reads as cold I/O and is not. func TestPrefixLookupUsesAnIndex(t *testing.T) { ix := openIndex(t) ci(t, ix, "abcdef0123456789", "trunk", "2026-01-01T10:00:00", "x") var plan string rows, err := ix.db.Query(`EXPLAIN QUERY PLAN SELECT hash FROM checkin WHERE hash >= ? AND hash < ?`, "abc", "abcg") if err != nil { t.Fatal(err) } defer rows.Close() for rows.Next() { var a, b, c int var detail string if err := rows.Scan(&a, &b, &c, &detail); err != nil { t.Fatal(err) } plan += detail + " " } if plan == "" { t.Fatal("no query plan") } if !contains(plan, "SEARCH") || contains(plan, "SCAN checkin") { t.Errorf("prefix lookup is not an index seek: %s", plan) } } func TestHexBoundRejectsNonHex(t *testing.T) { for _, ok := range []string{"abc", "0", "deadbeef", "0123456789abcdef"} { if _, good := hexBound(ok); !good { t.Errorf("hexBound(%q) refused a hex prefix", ok) } } // Anything that is not lowercase hex keeps the LIKE path, because // the 'g' terminator only bounds the hex alphabet. for _, bad := range []string{"", "ABC", "xyz", "abc-", "12 3"} { if _, good := hexBound(bad); good { t.Errorf("hexBound(%q) claimed a bound it cannot honour", bad) } } if hi, _ := hexBound("ff"); hi != "ffg" { t.Errorf("hexBound(ff) = %q, want ffg", hi) } } // The lookup still has to find things, by full hash and by prefix. func TestCheckinByPrefixStillResolves(t *testing.T) { ix := openIndex(t) ci(t, ix, "abcdef0123456789", "trunk", "2026-01-01T10:00:00", "the one") ci(t, ix, "bbbbbb9999999999", "trunk", "2026-01-02T10:00:00", "another") for _, p := range []string{"abcdef0123456789", "abcdef", "abc"} { got, _, ok, err := ix.CheckinByPrefix(p) if err != nil || !ok { t.Fatalf("prefix %q: ok=%v err=%v", p, ok, err) } if got.Comment != "the one" { t.Errorf("prefix %q resolved to %q", p, got.Comment) } } if _, _, ok, _ := ix.CheckinByPrefix("ffff"); ok { t.Error("a prefix matching nothing resolved anyway") } } func contains(h, n string) bool { for i := 0; i+len(n) <= len(h); i++ { if h[i:i+len(n)] == n { return true } } return false }