Artifact 9762d9a376bc7a3751800c0086c2deaadd474b18723674c4ca3f523f71a39b37:
- File
db/register.go
— part of check-in
[1fd12b3ed3]
at
2026-10-28 21:53:09
on branch feat/chunked-blobs
— Store blobs over a hosted value limit across blobpart rows
A hosted SQLite that caps value size (a Durable Object's 2 MB) cannot hold a large file's stored blob in one blob.content value. With the new db.DriverConfig.MaxValueBytes set, a stored value over the limit is written in 1,000,000-byte parts to blobpart(rid, seq, content), created on first use, leaving blob.content NULL and blob.size the expanded length, so phantoms (size < 0) stay unambiguous. Chunking sits below compression: hashes, deltas and the wire format are unchanged.
internal/blob owns the seam (ReadStored, WriteStored, InsertStored, StoredHead, DropStored, SweepParts and two scan expressions), and every direct reader or writer of blob.content now goes through it: delta rewrite and undelta, receive, clone send and walk, compaction, sparse demotion, seed drop, shun purge, horizon cost and cone preparation.
Zero, the default, keeps every value inline and creates no table, so local stores stay byte-identical. A store with blobpart rows is not readable by stock fossil; enable it only for hosted backends.
Closes #459 (user: [email protected], size: 1946) [annotate] [blame] [check-ins using]
package db // DriverConfig defines a SQLite driver's name and DSN builder. type DriverConfig struct { Name string BuildDSN func(path string, pragmas map[string]string) string // MaxValueBytes is the largest single value the backend accepts, for a // hosted SQLite (such as a Durable Object's) that caps value size. A // stored blob larger than this is written across rows of a blobpart // table instead of into blob.content; hashes, deltas and the wire // protocol are unchanged. Zero, the default, disables this: every value // is stored inline and no blobpart table is created, so a local store // stays byte-identical to stock fossil's. // // A store holding blobpart rows cannot be read by stock fossil, which // sees those blobs as NULL content. Set it only for hosted backends. // A nonzero value must be at least 1,000,000, the part size. MaxValueBytes int } var registered *DriverConfig // Register registers a SQLite driver for use by Open/OpenWith. // Must be called exactly once (typically from a driver package's init()). // Panics if called more than once. func Register(cfg DriverConfig) { if registered != nil { panic("db: driver already registered") } if cfg.Name == "" { panic("db: driver name must not be empty") } if cfg.BuildDSN == nil { panic("db: BuildDSN must not be nil") } // Parts are a fixed 1,000,000 bytes (internal/blob.PartBytes), so a // smaller limit could not hold even one of them. if cfg.MaxValueBytes < 0 || (cfg.MaxValueBytes > 0 && cfg.MaxValueBytes < 1_000_000) { panic("db: MaxValueBytes must be 0 or at least 1,000,000") } registered = &cfg if registered.Name == "" || registered.BuildDSN == nil { panic("db: registration postcondition failed") } } // RegisteredDriver returns a copy of the currently registered driver config, or nil if none. func RegisteredDriver() *DriverConfig { if registered == nil { return nil } cfg := *registered return &cfg }