Documentation
¶
Overview ¶
Package pager — PageBackend integrates the page-based storage engine with the tinySQL StorageBackend interface.
Package pager implements a page-based, transactional storage engine with Write-Ahead Logging (WAL) for tinySQL.
The storage format consists of a main database file (tinysql.db) with fixed-size pages (default 8 KiB) and a sequential WAL file (tinysql.wal). The first page is a superblock; subsequent pages are typed (B+Tree internal, B+Tree leaf, overflow, freelist). Every page carries a header with type, page-ID, LSN, and CRC32 checksum. Crash recovery replays committed WAL transactions from the last checkpoint_lsn.
Index ¶
- Constants
- func ComputePageCRC(page []byte) uint32
- func DumpTree(dbPath string, rootPageID PageID, pageSize int) (string, error)
- func FreeListCapacity(pageSize int) int
- func MarshalHeader(h *PageHeader, buf []byte)
- func MarshalRow(row []any, buf []byte) []byte
- func MarshalSuperblock(sb *Superblock, pageSize int) []byte
- func NewPage(pageSize int, pt PageType, id PageID) []byte
- func OverflowCapacity(pageSize int) int
- func ParseRowKey(key []byte) int64
- func RowKey(rowID int64) []byte
- func SetPageCRC(page []byte)
- func UnmarshalRow(data []byte) ([]any, error)
- func UnmarshalRowColumn(data []byte, column int) (any, error)
- func VerifyDB(dbPath string) ([]string, error)
- func VerifyPageCRC(page []byte) error
- type BTree
- func (bt *BTree) Contains(key []byte) (bool, error)
- func (bt *BTree) Count() (int, error)
- func (bt *BTree) Delete(txID TxID, key []byte) (bool, error)
- func (bt *BTree) FreeAllPages()
- func (bt *BTree) Get(key []byte) ([]byte, bool, error)
- func (bt *BTree) GetValue(key []byte, visit func(value []byte) error) (bool, error)
- func (bt *BTree) Insert(txID TxID, key, value []byte) error
- func (bt *BTree) Root() PageID
- func (bt *BTree) ScanRange(startKey, endKey []byte, fn func(key, value []byte) bool) error
- type BTreePage
- func (bp *BTreePage) Bytes() []byte
- func (bp *BTreePage) DeleteLeafEntry(pos int) error
- func (bp *BTreePage) FindChild(key []byte) PageID
- func (bp *BTreePage) FindLeafEntry(key []byte) (int, bool)
- func (bp *BTreePage) GetAllInternalEntries() []InternalEntry
- func (bp *BTreePage) GetAllLeafEntries() []LeafEntry
- func (bp *BTreePage) GetInternalEntry(i int) InternalEntry
- func (bp *BTreePage) GetLeafEntry(i int) LeafEntry
- func (bp *BTreePage) InsertInternalEntry(entry InternalEntry) error
- func (bp *BTreePage) InsertLeafEntry(entry LeafEntry) (int, error)
- func (bp *BTreePage) IsLeaf() bool
- func (bp *BTreePage) KeyCount() int
- func (bp *BTreePage) NextLeaf() PageID
- func (bp *BTreePage) PageID() PageID
- func (bp *BTreePage) PrevLeaf() PageID
- func (bp *BTreePage) RightChild() PageID
- func (bp *BTreePage) SearchInternal(key []byte) PageID
- func (bp *BTreePage) SetNextLeaf(pid PageID)
- func (bp *BTreePage) SetPrevLeaf(pid PageID)
- func (bp *BTreePage) SetRightChild(pid PageID)
- func (bp *BTreePage) UpdateLeafEntry(pos int, entry LeafEntry) error
- type BufferPoolConfig
- type Catalog
- type CatalogColumn
- type CatalogEntry
- type ColumnInfo
- type FeatureFlag
- type FreeListPage
- func (fl *FreeListPage) AddEntry(pid PageID) bool
- func (fl *FreeListPage) AllEntries() []PageID
- func (fl *FreeListPage) Bytes() []byte
- func (fl *FreeListPage) EntryCount() int
- func (fl *FreeListPage) GetEntry(i int) PageID
- func (fl *FreeListPage) NextFreeList() PageID
- func (fl *FreeListPage) PopEntry() PageID
- func (fl *FreeListPage) SetNextFreeList(pid PageID)
- type FreeManager
- func (fm *FreeManager) AllFree() []PageID
- func (fm *FreeManager) Alloc() PageID
- func (fm *FreeManager) Count() int
- func (fm *FreeManager) FlushToDisk(pageSize int, allocPage func() (PageID, []byte)) (PageID, [][]byte)
- func (fm *FreeManager) Free(pid PageID)
- func (fm *FreeManager) LoadFromDisk(head PageID, readPage func(PageID) ([]byte, error)) error
- type GCResult
- type IndexEntry
- type IndexInfo
- type InternalEntry
- type LSN
- type LeafEntry
- type OverflowPage
- type PageBackend
- func (pb *PageBackend) AppendRows(tenant, name string, newRows [][]any, newIndexKeys map[string][][]byte) (int64, error)
- func (pb *PageBackend) Close() error
- func (pb *PageBackend) DBPath() string
- func (pb *PageBackend) DeleteTable(tenant, name string) error
- func (pb *PageBackend) GC() (*GCResult, error)
- func (pb *PageBackend) ListTableNames(tenant string) ([]string, error)
- func (pb *PageBackend) LoadTable(tenant, name string) (*TableData, error)
- func (pb *PageBackend) LookupIndexRows(tenant, table, indexName string, key []byte) ([][]any, bool, error)
- func (pb *PageBackend) LookupIndexRowsByRoot(tableRoot, indexRoot PageID, indexName string, key []byte) ([][]any, bool, error)
- func (pb *PageBackend) LookupUniqueIndexRowBytesColumnByRoot(tableRoot, indexRoot PageID, indexName string, key []byte, column int, ...) (bool, error)
- func (pb *PageBackend) LookupUniqueIndexRowColumnByRoot(tableRoot, indexRoot PageID, indexName string, key []byte, column int, ...) (bool, error)
- func (pb *PageBackend) LookupUniqueIndexRowExistsByRoot(tableRoot, indexRoot PageID, indexName string, key []byte) (bool, error)
- func (pb *PageBackend) MaxCachePages() int
- func (pb *PageBackend) Pager() *Pager
- func (pb *PageBackend) SaveTable(tenant string, td *TableData) error
- func (pb *PageBackend) ScanIndexRowsRange(tenant, table, indexName string, startKey, endKey []byte, fn func([]any) bool) error
- func (pb *PageBackend) ScanIndexRowsRangeBatch(tenant, table, indexName string, startKey, endKey, resumeKey []byte, ...) (rows [][]any, nextKey []byte, nextSkip int, done bool, err error)
- func (pb *PageBackend) ScanTableRows(tenant, name string, fn func(row []any) bool) error
- func (pb *PageBackend) ScanTableRowsBatch(tenant, name string, afterKey []byte, limit int) (rows [][]any, nextKey []byte, done bool, err error)
- func (pb *PageBackend) Stats() PageBackendStats
- func (pb *PageBackend) Sync() error
- func (pb *PageBackend) TableEntry(tenant, table string) (*CatalogEntry, error)
- func (pb *PageBackend) TableExists(tenant, name string) bool
- type PageBackendConfig
- type PageBackendStats
- type PageBufferPool
- type PageFrame
- type PageHeader
- type PageID
- type PageInfo
- type PageType
- type Pager
- func (p *Pager) AbortTx(txID TxID) error
- func (p *Pager) AllocPage() (PageID, []byte)
- func (p *Pager) BeginTx() (TxID, error)
- func (p *Pager) CacheStats() PagerCacheStats
- func (p *Pager) Checkpoint() error
- func (p *Pager) Close() error
- func (p *Pager) CommitTx(txID TxID) error
- func (p *Pager) FreePage(pid PageID)
- func (p *Pager) PageSize() int
- func (p *Pager) Path() string
- func (p *Pager) ReadPage(id PageID) ([]byte, error)
- func (p *Pager) Recover() error
- func (p *Pager) Superblock() Superblock
- func (p *Pager) UnpinPage(id PageID)
- func (p *Pager) UpdateSuperblock(fn func(sb *Superblock))
- func (p *Pager) WALPath() string
- func (p *Pager) WritePage(txID TxID, id PageID, buf []byte) error
- type PagerCacheStats
- type PagerConfig
- type SlotEntry
- type SlottedPage
- func (sp *SlottedPage) Bytes() []byte
- func (sp *SlottedPage) Compact()
- func (sp *SlottedPage) DeleteRecord(i int) error
- func (sp *SlottedPage) FreeSpace() int
- func (sp *SlottedPage) FreeSpaceEnd() int
- func (sp *SlottedPage) GetRecord(i int) []byte
- func (sp *SlottedPage) GetSlot(i int) SlotEntry
- func (sp *SlottedPage) InsertRecord(data []byte) (int, error)
- func (sp *SlottedPage) IsDeleted(i int) bool
- func (sp *SlottedPage) LiveRecords() int
- func (sp *SlottedPage) SlotCount() int
- func (sp *SlottedPage) UpdateRecord(i int, data []byte) error
- type Superblock
- type SuperblockInfo
- type TableData
- type TxID
- type WALFile
- type WALInfo
- type WALRecord
- type WALRecordType
Constants ¶
const ( // DefaultPageSize is the default page size in bytes (8 KiB). DefaultPageSize = 8192 // MinPageSize is the minimum allowed page size (4 KiB). MinPageSize = 4096 // MaxPageSize is the maximum allowed page size (64 KiB). MaxPageSize = 65536 // PageHeaderSize is the size of the common page header in bytes. // Layout: // [0] PageType (1 byte) // [1] Flags (1 byte) // [2:4] Reserved (2 bytes) // [4:8] PageID (4 bytes, uint32 LE) // [8:16] LSN (8 bytes, uint64 LE) // [16:20] CRC32 (4 bytes, uint32 LE) // [20:32] Reserved (12 bytes) PageHeaderSize = 32 // InvalidPageID represents a null/invalid page pointer. InvalidPageID PageID = 0 // OverflowThreshold is the max inline value size (bytes) before an // overflow page chain is used. Set to 1/4 of usable leaf space. // Adjusted dynamically based on page size, but this is the default. OverflowThreshold = 1024 )
const ( // SuperblockMagic identifies a valid tinySQL database file. SuperblockMagic = "TNSQLDB\x00" // CurrentFormatVersion is the on-disk format version. CurrentFormatVersion uint32 = 1 )
const ( WALMagic = "TNSQWAL\x00" WALVersion = uint32(1) WALFileHdrSize = 32 WALRecHdrSize = 33 )
const MaxValueBytes = 64 << 20
MaxValueBytes is the defensive decoder bound for one variable-length row field. SQL writes are capped earlier by engine.MaxBlobBytes; this second boundary keeps damaged on-disk lengths from allocating excessive memory.
Variables ¶
This section is empty.
Functions ¶
func ComputePageCRC ¶
ComputePageCRC computes the CRC32-C of a full page, treating the CRC field (bytes 16..20) as zero during computation.
func FreeListCapacity ¶
FreeListCapacity returns how many page IDs fit in one free-list page.
func MarshalHeader ¶
func MarshalHeader(h *PageHeader, buf []byte)
MarshalHeader writes a PageHeader into the first PageHeaderSize bytes of buf.
func MarshalRow ¶
MarshalRow encodes a row into the compact binary format. It reuses the provided buf if large enough.
func MarshalSuperblock ¶
func MarshalSuperblock(sb *Superblock, pageSize int) []byte
MarshalSuperblock serializes a Superblock into a full page buffer. The buffer must be at least PageSize bytes. The common PageHeader is set (Type=Superblock, ID=0) and the CRC computed.
func OverflowCapacity ¶
OverflowCapacity returns the payload capacity of a single overflow page.
func ParseRowKey ¶
ParseRowKey extracts the row ID from a B+Tree key.
func SetPageCRC ¶
func SetPageCRC(page []byte)
SetPageCRC computes and writes the CRC into the page header.
func UnmarshalRow ¶
UnmarshalRow decodes a row from the compact binary format.
func UnmarshalRowColumn ¶ added in v0.28.0
UnmarshalRowColumn decodes only one requested column. Columns before it are bounds-checked and skipped without constructing interface values. This is useful for typed index readers that need one BLOB or identifier from a wide row and have already validated the complete immutable artifact at open.
func VerifyDB ¶
VerifyDB checks the integrity of an entire database file. Returns a list of issues found (empty = healthy).
func VerifyPageCRC ¶
VerifyPageCRC checks the CRC32 checksum of a page.
Types ¶
type BTree ¶
type BTree struct {
// contains filtered or unexported fields
}
BTree represents a B+Tree stored in the pager.
func CreateBTree ¶
CreateBTree allocates a new B+Tree with an empty leaf root page. Must be called within a transaction.
func NewBTree ¶
NewBTree creates a handle to an existing B+Tree with the given root. For a new tree, call CreateBTree first.
func (*BTree) Contains ¶ added in v0.28.1
Contains reports whether key exists without loading or assembling its value. This matters for large overflow-backed rows when an index validator only needs to prove that a row locator resolves to a physical table entry.
func (*BTree) FreeAllPages ¶
func (bt *BTree) FreeAllPages()
FreeAllPages recursively frees every page owned by this B+Tree (internal nodes, leaf nodes, and overflow chains). After this call the tree is invalid and must not be used.
func (*BTree) Get ¶
Get looks up a key. Returns (value, true) or (nil, false). Handles overflow pages transparently.
func (*BTree) GetValue ¶ added in v0.20.0
GetValue resolves key and invokes visit while the containing page remains pinned. Inline values are views into that page and must not be retained by visit. Overflow values are assembled into a temporary slice. This allows index locators and row codecs to decode directly from page memory without allocating a throw-away copy for every B+Tree comparison.
type BTreePage ¶
type BTreePage struct {
// contains filtered or unexported fields
}
BTreePage wraps a page buffer as a B+Tree node.
func InitBTreePage ¶
InitBTreePage initialises a page as a B+Tree node.
func WrapBTreePage ¶
WrapBTreePage wraps an existing buffer.
func (*BTreePage) DeleteLeafEntry ¶
DeleteLeafEntry removes the entry at position pos.
func (*BTreePage) FindChild ¶
FindChild returns the child PageID for the given search key. For internal-page navigation: find the largest key <= searchKey.
func (*BTreePage) FindLeafEntry ¶
FindLeafEntry searches for an exact key match. Returns (index, true) or (-1, false).
func (*BTreePage) GetAllInternalEntries ¶
func (bp *BTreePage) GetAllInternalEntries() []InternalEntry
GetAllInternalEntries returns all separator entries in order.
func (*BTreePage) GetAllLeafEntries ¶
GetAllLeafEntries returns all leaf entries in order.
func (*BTreePage) GetInternalEntry ¶
func (bp *BTreePage) GetInternalEntry(i int) InternalEntry
GetInternalEntry returns the i-th separator key and its left child.
func (*BTreePage) GetLeafEntry ¶
GetLeafEntry returns the i-th key-value pair.
func (*BTreePage) InsertInternalEntry ¶
func (bp *BTreePage) InsertInternalEntry(entry InternalEntry) error
InsertInternalEntry inserts a separator key at the correct sorted position.
func (*BTreePage) InsertLeafEntry ¶
InsertLeafEntry inserts a key-value pair at the correct sorted position. Returns the slot index.
func (*BTreePage) RightChild ¶
RightChild returns the rightmost child page of an internal node.
func (*BTreePage) SearchInternal ¶
SearchInternal finds the child page for a given key in an internal node. Internal page layout: entries[0].child, entries[0].key, entries[1].child, ... Keys divide the key space: key < entry[0].key → entry[0].child, entry[i-1].key <= key < entry[i].key → entry[i].child, key >= entry[last].key → RightChild.
func (*BTreePage) SetNextLeaf ¶
SetNextLeaf updates the next-leaf link.
func (*BTreePage) SetPrevLeaf ¶
SetPrevLeaf updates the previous-leaf link.
func (*BTreePage) SetRightChild ¶
SetRightChild updates the rightmost child page of an internal node.
type BufferPoolConfig ¶
type BufferPoolConfig struct {
MaxPages int // maximum number of cached pages (default 1024)
}
BufferPoolConfig configures the page buffer pool.
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog manages the system catalog B+Tree.
func OpenCatalog ¶
OpenCatalog opens or creates the system catalog.
func (*Catalog) DeleteEntry ¶
DeleteEntry removes a catalog entry within the given transaction.
func (*Catalog) GetEntry ¶
func (c *Catalog) GetEntry(tenant, table string) (*CatalogEntry, error)
GetEntry retrieves a catalog entry. Returns nil if not found.
func (*Catalog) ListTables ¶
ListTables returns all table names for a tenant.
type CatalogColumn ¶
type CatalogColumn struct {
NotNull bool `json:"not_null,omitempty"`
StrictJSON bool `json:"strict_json,omitempty"`
Name string `json:"name"`
Type int `json:"type"` // ColType as int
Constraint int `json:"constraint"` // ConstraintType as int
FKTable string `json:"fk_table,omitempty"`
FKColumn string `json:"fk_col,omitempty"`
PtrTable string `json:"ptr_table,omitempty"`
}
CatalogColumn describes a column in the system catalog.
type CatalogEntry ¶
type CatalogEntry struct {
Tenant string `json:"tenant"`
Table string `json:"table"`
RootPageID PageID `json:"root_page_id"`
Columns []CatalogColumn `json:"columns"`
Indexes []IndexInfo `json:"indexes,omitempty"`
RowCount int64 `json:"row_count"`
Version int `json:"version"`
StructVersion int `json:"struct_version,omitempty"`
FTSIndexes []byte `json:"fts_indexes,omitempty"`
VectorIndexes []byte `json:"vector_indexes,omitempty"`
}
CatalogEntry is the value stored in the system catalog B+Tree.
type ColumnInfo ¶
type ColumnInfo struct {
Name string
Type int // ColType as int
Constraint int // ConstraintType as int
FKTable string
FKColumn string
PointerTable string
NotNull bool
StrictJSON bool
}
ColumnInfo is a simplified, pager-internal column descriptor that does not import the storage package (to avoid circular dependencies).
type FeatureFlag ¶
type FeatureFlag uint64
FeatureFlag is a bitmask of optional format features.
const ( FeatureCompression FeatureFlag = 1 << iota // reserved: page-level compression FeatureEncryption // reserved: page-level encryption FeatureMVCC // reserved: multi-version concurrency FeaturePartitions // reserved: range partitioning )
FeatureFlag bits (bitmask). Version 1 has no flags set.
const SupportedFeatures FeatureFlag = 0 // v1: none
SupportedFeatures is the set of features understood by this build. Any flag outside of this set causes the file to be rejected.
type FreeListPage ¶
type FreeListPage struct {
// contains filtered or unexported fields
}
FreeListPage wraps a page buffer as a free-list page.
func InitFreeListPage ¶
func InitFreeListPage(buf []byte, id PageID) *FreeListPage
InitFreeListPage creates a new empty free-list page.
func WrapFreeListPage ¶
func WrapFreeListPage(buf []byte) *FreeListPage
WrapFreeListPage wraps an existing free-list buffer.
func (*FreeListPage) AddEntry ¶
func (fl *FreeListPage) AddEntry(pid PageID) bool
AddEntry appends a free page ID. Returns false if the page is full.
func (*FreeListPage) AllEntries ¶
func (fl *FreeListPage) AllEntries() []PageID
AllEntries returns all stored free page IDs.
func (*FreeListPage) Bytes ¶
func (fl *FreeListPage) Bytes() []byte
Bytes returns the underlying page buffer.
func (*FreeListPage) EntryCount ¶
func (fl *FreeListPage) EntryCount() int
EntryCount returns the number of free page IDs stored.
func (*FreeListPage) GetEntry ¶
func (fl *FreeListPage) GetEntry(i int) PageID
GetEntry returns the i-th free page ID.
func (*FreeListPage) NextFreeList ¶
func (fl *FreeListPage) NextFreeList() PageID
NextFreeList returns the next free-list page in the chain.
func (*FreeListPage) PopEntry ¶
func (fl *FreeListPage) PopEntry() PageID
PopEntry removes and returns the last entry. Returns InvalidPageID if empty.
func (*FreeListPage) SetNextFreeList ¶
func (fl *FreeListPage) SetNextFreeList(pid PageID)
SetNextFreeList sets the next page pointer.
type FreeManager ¶
type FreeManager struct {
// contains filtered or unexported fields
}
FreeManager tracks free pages using an in-memory set backed by free-list pages on disk. The pager calls its methods during allocation and deallocation.
func NewFreeManager ¶
func NewFreeManager() *FreeManager
NewFreeManager creates a FreeManager. Call LoadFromDisk to populate.
func (*FreeManager) AllFree ¶
func (fm *FreeManager) AllFree() []PageID
AllFree returns all free page IDs (unsorted).
func (*FreeManager) Alloc ¶
func (fm *FreeManager) Alloc() PageID
Alloc returns a free page ID (popped from the set) or InvalidPageID if empty.
func (*FreeManager) Count ¶
func (fm *FreeManager) Count() int
Count returns the number of free pages.
func (*FreeManager) FlushToDisk ¶
func (fm *FreeManager) FlushToDisk(pageSize int, allocPage func() (PageID, []byte)) (PageID, [][]byte)
FlushToDisk writes the in-memory free set into free-list pages. It returns the head PageID of the new chain and the list of page buffers to write. allocPage is a callback that returns a new, zeroed page buffer with a fresh ID.
func (*FreeManager) Free ¶
func (fm *FreeManager) Free(pid PageID)
Free marks a page ID as available for reuse.
func (*FreeManager) LoadFromDisk ¶
LoadFromDisk walks the free-list chain starting at head and populates the in-memory set. readPage is a callback that reads a page by ID.
type GCResult ¶
type GCResult struct {
TotalPages int // total allocated pages in the file
ReachablePages int // pages reachable from roots
FreeBefore int // free pages before GC
FreeAfter int // free pages after GC
Reclaimed int // newly freed orphan pages
Errors []string // non-fatal issues found during the scan
}
GCResult holds statistics about a garbage collection run.
type IndexEntry ¶ added in v0.20.0
IndexEntry maps one canonical key to row IDs in table order.
type IndexInfo ¶ added in v0.20.0
type IndexInfo struct {
Name string `json:"name"`
Columns []string `json:"columns"`
Unique bool `json:"unique"`
RootPageID PageID `json:"root_page_id"`
Entries []IndexEntry `json:"-"`
}
IndexInfo identifies one persistent secondary-index B+Tree. Entries are supplied only while building a table; root and schema are persisted in the catalog and are sufficient for immutable seeks after reopening the file.
type InternalEntry ¶
InternalEntry represents a key + left-child pointer for internal pages.
type LeafEntry ¶
type LeafEntry struct {
Key []byte
Value []byte // inline value (empty when overflow)
Overflow bool
OverflowPageID PageID
TotalSize uint32
}
LeafEntry represents a key-value pair stored in a leaf page.
type OverflowPage ¶
type OverflowPage struct {
// contains filtered or unexported fields
}
OverflowPage wraps a page buffer as an overflow page.
func InitOverflowPage ¶
func InitOverflowPage(buf []byte, id PageID) *OverflowPage
InitOverflowPage creates a new overflow page.
func WrapOverflowPage ¶
func WrapOverflowPage(buf []byte) *OverflowPage
WrapOverflowPage wraps an existing overflow page buffer.
func (*OverflowPage) Bytes ¶
func (op *OverflowPage) Bytes() []byte
Bytes returns the underlying page buffer.
func (*OverflowPage) DataLen ¶
func (op *OverflowPage) DataLen() int
DataLen returns the number of payload bytes stored.
func (*OverflowPage) NextOverflow ¶
func (op *OverflowPage) NextOverflow() PageID
NextOverflow returns the next overflow page in the chain.
func (*OverflowPage) SetData ¶
func (op *OverflowPage) SetData(data []byte) error
SetData writes payload into the overflow page. Returns an error if the data exceeds the capacity.
func (*OverflowPage) SetNextOverflow ¶
func (op *OverflowPage) SetNextOverflow(pid PageID)
SetNextOverflow sets the next-page pointer.
type PageBackend ¶
type PageBackend struct {
// contains filtered or unexported fields
}
PageBackend implements a disk-based key-value store backed by B+Trees, a WAL for crash safety, and a buffer pool for caching.
func NewPageBackend ¶
func NewPageBackend(cfg PageBackendConfig) (*PageBackend, error)
NewPageBackend opens or creates a page-based database.
func (*PageBackend) AppendRows ¶ added in v0.26.2
func (pb *PageBackend) AppendRows(tenant, name string, newRows [][]any, newIndexKeys map[string][][]byte) (int64, error)
AppendRows inserts newRows into an existing table's B+Tree in place, without reading or rewriting any row already on disk, and inserts newIndexKeys into the existing unique secondary-index B+Trees the same way.
This is what SaveTable cannot do: SaveTable always frees the whole existing tree and rebuilds it from a complete row set, so its cost -- and the memory needed to hold that complete row set -- grows with the table's total size. Building a tileset far larger than memory means every batch after the first must cost only its own size, not the size of everything imported so far; AppendRows is the primitive that makes that possible. See ImportMBTiles in internal/importer, which uses it when the destination is paged-index storage.
newIndexKeys maps index name to one canonical key per row in newRows, in the same order; a nil/empty key skips that row for that index. Only unique indexes are meaningful here: a duplicate key is rejected by neither this method nor the underlying B+Tree (which stores the last write), so the caller is responsible for uniqueness -- true for a well-formed MBTiles source, whose own UNIQUE index already guarantees it.
Returns the row ID assigned to newRows[0]; subsequent rows are numbered sequentially from there.
func (*PageBackend) Close ¶
func (pb *PageBackend) Close() error
Close performs a final checkpoint and closes all files.
func (*PageBackend) DBPath ¶
func (pb *PageBackend) DBPath() string
DBPath returns the database file path.
func (*PageBackend) DeleteTable ¶
func (pb *PageBackend) DeleteTable(tenant, name string) error
DeleteTable removes a table from the catalog and frees its pages.
func (*PageBackend) GC ¶
func (pb *PageBackend) GC() (*GCResult, error)
GC performs a full reachability-based garbage collection on the database. It must be called when no other writers are active (exclusive access). The GC does NOT shrink the file — it only adds orphans to the free-list so they can be reused by future writes.
func (*PageBackend) ListTableNames ¶
func (pb *PageBackend) ListTableNames(tenant string) ([]string, error)
ListTableNames returns all table names for a tenant.
func (*PageBackend) LoadTable ¶
func (pb *PageBackend) LoadTable(tenant, name string) (*TableData, error)
LoadTable retrieves all rows of a table from its B+Tree.
func (*PageBackend) LookupIndexRows ¶ added in v0.20.0
func (pb *PageBackend) LookupIndexRows(tenant, table, indexName string, key []byte) ([][]any, bool, error)
LookupIndexRows follows an immutable secondary-index B+Tree and loads only referenced row records from the table B+Tree. It intentionally bypasses LoadTable, which materializes all rows for compatibility callers.
func (*PageBackend) LookupIndexRowsByRoot ¶ added in v0.20.0
func (pb *PageBackend) LookupIndexRowsByRoot(tableRoot, indexRoot PageID, indexName string, key []byte) ([][]any, bool, error)
LookupIndexRowsByRoot is the locator form of LookupIndexRows. A caller that has already validated an immutable catalog entry can bypass the catalog B+Tree and JSON decode on every point lookup. Roots are only accepted from the same open PageBackend; they are not a portable public file-format API.
func (*PageBackend) LookupUniqueIndexRowBytesColumnByRoot ¶ added in v0.33.0
func (pb *PageBackend) LookupUniqueIndexRowBytesColumnByRoot(tableRoot, indexRoot PageID, indexName string, key []byte, column int, visit func([]byte) error) (bool, error)
LookupUniqueIndexRowBytesColumnByRoot is the concrete-BLOB variant of LookupUniqueIndexRowColumnByRoot. It is for typed serving paths that always read a BLOB column (notably immutable tile artifacts), avoiding interface boxing and a type assertion per request.
func (*PageBackend) LookupUniqueIndexRowColumnByRoot ¶ added in v0.28.0
func (pb *PageBackend) LookupUniqueIndexRowColumnByRoot(tableRoot, indexRoot PageID, indexName string, key []byte, column int, visit func(any) error) (bool, error)
LookupUniqueIndexRowColumnByRoot resolves one unique index entry and decodes only the requested table column. It avoids allocating a row-ID slice, an outer rows slice and values for columns the typed caller does not consume.
func (*PageBackend) LookupUniqueIndexRowExistsByRoot ¶ added in v0.28.1
func (pb *PageBackend) LookupUniqueIndexRowExistsByRoot(tableRoot, indexRoot PageID, indexName string, key []byte) (bool, error)
LookupUniqueIndexRowExistsByRoot follows a unique index locator and proves that its table row exists without reading the row value. It validates the same durable 12-byte locator used by typed lookups while avoiding overflow BLOB I/O for integrity checks that need reachability only.
func (*PageBackend) MaxCachePages ¶ added in v0.20.0
func (pb *PageBackend) MaxCachePages() int
MaxCachePages reports the strict page-cache capacity used by this backend.
func (*PageBackend) Pager ¶
func (pb *PageBackend) Pager() *Pager
Pager returns the underlying pager (for inspection tools).
func (*PageBackend) SaveTable ¶
func (pb *PageBackend) SaveTable(tenant string, td *TableData) error
SaveTable persists all rows of a table into a B+Tree. This replaces the entire table contents (drop + recreate of the tree).
func (*PageBackend) ScanIndexRowsRange ¶ added in v0.27.1
func (pb *PageBackend) ScanIndexRowsRange(tenant, table, indexName string, startKey, endKey []byte, fn func([]any) bool) error
ScanIndexRowsRange streams rows selected by an ordered composite index. It is the range counterpart to LookupIndexRows and deliberately invokes the callback per row so spatial windows do not materialize a whole tile band.
func (*PageBackend) ScanIndexRowsRangeBatch ¶ added in v0.46.0
func (pb *PageBackend) ScanIndexRowsRangeBatch(tenant, table, indexName string, startKey, endKey, resumeKey []byte, skipAtResumeKey, limit int) (rows [][]any, nextKey []byte, nextSkip int, done bool, err error)
ScanIndexRowsRangeBatch decodes at most limit table rows from an ordered secondary-index interval. It resumes at (resumeKey, skipAtResumeKey), where the skip count is necessary because one index key can point to many rows. Like ScanTableRowsBatch, it never invokes user code while pb.mu is held.
A full batch may conservatively leave done false even at EOF. Calling it once more with the returned cursor establishes EOF without dropping rows.
func (*PageBackend) ScanTableRows ¶ added in v0.26.2
func (pb *PageBackend) ScanTableRows(tenant, name string, fn func(row []any) bool) error
ScanTableRows streams a table's rows to fn in key order, one B+Tree leaf page at a time, instead of collecting them all into one slice the way LoadTable does. fn returning false stops the scan early, same as BTree.ScanRange. Memory use is bounded by one leaf's worth of rows (plus whatever overflow pages a single oversized row needs), not by the table's total size -- the reader side of the same "larger than memory" property AppendRows gives the writer side. ExportMBTiles (internal/importer) is the motivating caller: writing out a tileset larger than memory should not need loading it into memory first.
func (*PageBackend) ScanTableRowsBatch ¶ added in v0.46.0
func (pb *PageBackend) ScanTableRowsBatch(tenant, name string, afterKey []byte, limit int) (rows [][]any, nextKey []byte, done bool, err error)
ScanTableRowsBatch decodes at most limit rows after afterKey and releases the backend read lock before returning them to the caller. It is the cursor building block for slow network result streams: unlike ScanTableRows, a blocked consumer can never retain pb.mu's read side while it waits.
nextKey is the final returned table key. When done is false, pass it as afterKey to continue. A full final batch can conservatively report done false; one cheap empty follow-up call then establishes EOF without risking a missed last row.
func (*PageBackend) Stats ¶
func (pb *PageBackend) Stats() PageBackendStats
Stats returns operational statistics.
func (*PageBackend) TableEntry ¶ added in v0.20.0
func (pb *PageBackend) TableEntry(tenant, table string) (*CatalogEntry, error)
TableEntry returns table and index metadata without scanning row pages.
func (*PageBackend) TableExists ¶
func (pb *PageBackend) TableExists(tenant, name string) bool
TableExists reports whether a table exists in the catalog.
type PageBackendConfig ¶
type PageBackendConfig struct {
Path string // database file path (.db)
PageSize int // 0 = DefaultPageSize (8 KiB)
MaxCachePages int // buffer pool size (0 = default 1024)
ReadOnly bool
}
PageBackendConfig configures the page-based StorageBackend.
type PageBackendStats ¶
type PageBackendStats struct {
PageSize int
PageCount uint64
FreePages int
CheckpointLSN LSN
NextTxID TxID
SyncCount int64
LoadCount int64
PageReads int64
CacheHits int64
CacheMisses int64
CachedPages int
PinnedPages int
TransientPages int
TransientFrames int
MaxCachePages int
DBPath string
WALPath string
}
PageBackendStats holds operational metrics.
type PageBufferPool ¶
type PageBufferPool struct {
// contains filtered or unexported fields
}
PageBufferPool is an LRU page cache with dirty-page tracking.
type PageFrame ¶
type PageFrame struct {
// contains filtered or unexported fields
}
PageFrame is an in-memory cached page.
type PageHeader ¶
type PageHeader struct {
Type PageType // 1 byte
Flags uint8 // 1 byte
Reserved uint16 // 2 bytes
ID PageID // 4 bytes
LSN LSN // 8 bytes
CRC uint32 // 4 bytes — CRC32 of the entire page (with CRC field zeroed)
Pad [12]byte // reserved for future use
}
PageHeader is the 32-byte header present at the start of every page.
func UnmarshalHeader ¶
func UnmarshalHeader(buf []byte) PageHeader
UnmarshalHeader reads a PageHeader from the first PageHeaderSize bytes of buf.
type PageID ¶
type PageID uint32
PageID is a 32-bit page identifier. Page 0 is always the superblock.
type PageInfo ¶
type PageInfo struct {
ID PageID
Type PageType
TypeStr string
LSN LSN
CRC uint32
CRCValid bool
Flags uint8
// B+Tree specifics
IsLeaf bool
KeyCount int
RightChild PageID
NextLeaf PageID
PrevLeaf PageID
// Slotted page stats
SlotCount int
FreeSpace int
// Overflow
NextOverflow PageID
DataLen int
// FreeList
NextFreeList PageID
EntryCount int
}
PageInfo holds inspection information about a single page.
type Pager ¶
type Pager struct {
// contains filtered or unexported fields
}
Pager manages page-level I/O, WAL, buffer pool, and free-list.
func OpenPager ¶
func OpenPager(cfg PagerConfig) (*Pager, error)
OpenPager opens or creates a page-based database.
func (*Pager) AbortTx ¶
AbortTx writes an ABORT record. Dirty pages for this TX will be discarded on the next recovery or checkpoint.
func (*Pager) AllocPage ¶
AllocPage allocates a new page (from the free-list or by extending the file). Returns the page ID and a zeroed buffer. The page is pinned in the cache.
func (*Pager) CacheStats ¶ added in v0.20.0
func (p *Pager) CacheStats() PagerCacheStats
CacheStats returns a snapshot of the pager's in-memory page cache.
func (*Pager) Checkpoint ¶
Checkpoint flushes all dirty pages to the database file, writes an updated superblock, fsyncs the file, then truncates the WAL.
func (*Pager) ReadPage ¶
ReadPage returns a page by ID, using the buffer pool cache. The page is pinned in the cache; call UnpinPage when done.
func (*Pager) Superblock ¶
func (p *Pager) Superblock() Superblock
Superblock returns a copy of the current superblock.
func (*Pager) UpdateSuperblock ¶
func (p *Pager) UpdateSuperblock(fn func(sb *Superblock))
UpdateSuperblock updates the in-memory superblock fields. It does NOT write to disk. Use Checkpoint for that.
type PagerCacheStats ¶ added in v0.20.0
type PagerCacheStats struct {
PageReads int64
CacheHits int64
CacheMisses int64
CachedPages int
PinnedPages int
TransientPages int
TransientFrames int
MaxPages int
}
PagerCacheStats reports the physical-read and bounded-cache state. The counters are deliberately process-local: they describe this open snapshot, not a persisted database property.
type PagerConfig ¶
type PagerConfig struct {
DBPath string
WALPath string
PageSize int
MaxCachePages int // buffer pool capacity (0 = default 1024)
ReadOnly bool
}
PagerConfig configures a Pager.
type SlottedPage ¶
type SlottedPage struct {
// contains filtered or unexported fields
}
SlottedPage wraps a raw page buffer and provides record-level operations.
func InitSlottedPage ¶
func InitSlottedPage(buf []byte, pt PageType, id PageID) *SlottedPage
InitSlottedPage initialises a page buffer as an empty slotted page.
func WrapSlottedPage ¶
func WrapSlottedPage(buf []byte) *SlottedPage
WrapSlottedPage wraps an existing page buffer.
func (*SlottedPage) Bytes ¶
func (sp *SlottedPage) Bytes() []byte
Bytes returns the underlying page buffer.
func (*SlottedPage) Compact ¶
func (sp *SlottedPage) Compact()
Compact reorganises records to remove gaps left by deletions. Preserves slot order. This is needed before splitting pages.
func (*SlottedPage) DeleteRecord ¶
func (sp *SlottedPage) DeleteRecord(i int) error
DeleteRecord marks slot i as deleted (tombstone).
func (*SlottedPage) FreeSpace ¶
func (sp *SlottedPage) FreeSpace() int
FreeSpace returns the number of bytes available for new records+slots.
func (*SlottedPage) FreeSpaceEnd ¶
func (sp *SlottedPage) FreeSpaceEnd() int
FreeSpaceEnd is the byte offset where the next record will be written.
func (*SlottedPage) GetRecord ¶
func (sp *SlottedPage) GetRecord(i int) []byte
GetRecord returns the raw bytes of the record at slot i. Returns nil if the slot is a tombstone.
func (*SlottedPage) GetSlot ¶
func (sp *SlottedPage) GetSlot(i int) SlotEntry
GetSlot returns the slot entry at index i.
func (*SlottedPage) InsertRecord ¶
func (sp *SlottedPage) InsertRecord(data []byte) (int, error)
InsertRecord adds a new record to the page. Returns the slot index, or an error if there is insufficient space.
func (*SlottedPage) IsDeleted ¶
func (sp *SlottedPage) IsDeleted(i int) bool
IsDeleted returns true if slot i is a tombstone.
func (*SlottedPage) LiveRecords ¶
func (sp *SlottedPage) LiveRecords() int
LiveRecords returns the count of non-deleted records.
func (*SlottedPage) SlotCount ¶
func (sp *SlottedPage) SlotCount() int
SlotCount returns the number of slots (including tombstones).
func (*SlottedPage) UpdateRecord ¶
func (sp *SlottedPage) UpdateRecord(i int, data []byte) error
UpdateRecord replaces the record at slot i. If the new data fits in the old slot's space, it is written in-place; otherwise the old slot is tombstoned and a new record is appended.
type Superblock ¶
type Superblock struct {
FormatVersion uint32
PageSize uint32
PageCount uint64
FeatureFlags FeatureFlag
CatalogRoot PageID
FreeListRoot PageID
CheckpointLSN LSN
NextTxID TxID
NextPageID PageID
}
Superblock holds the parsed contents of page 0.
func NewSuperblock ¶
func NewSuperblock(pageSize uint32) *Superblock
NewSuperblock creates a default Superblock for a new database.
func UnmarshalSuperblock ¶
func UnmarshalSuperblock(buf []byte) (*Superblock, error)
UnmarshalSuperblock decodes page 0 from buf. It validates magic bytes, format version, feature flags, and CRC. Returns an error on any mismatch.
type SuperblockInfo ¶
type SuperblockInfo struct {
FormatVersion uint32
PageSize uint32
PageCount uint64
FeatureFlags uint64
CatalogRoot PageID
FreeListRoot PageID
CheckpointLSN LSN
NextTxID TxID
NextPageID PageID
CRCValid bool
}
SuperblockInfo holds display-friendly superblock data.
func InspectSuperblock ¶
func InspectSuperblock(dbPath string) (*SuperblockInfo, error)
InspectSuperblock reads and returns the superblock metadata.
type TableData ¶
type TableData struct {
Name string
Columns []ColumnInfo
Rows [][]any
Indexes []IndexInfo
IsTemp bool
Version int
StructVersion int
FTSIndexes []byte
VectorIndexes []byte
}
TableData is the pager-level representation of a table. Higher layers convert between TableData and storage.Table.
type WALFile ¶
type WALFile struct {
// contains filtered or unexported fields
}
WALFile manages the append-only WAL file.
func OpenWALFile ¶
OpenWALFile opens or creates a WAL file. If the file exists, it validates the header. If it does not exist, it writes a new header.
func (*WALFile) AppendRecord ¶
AppendRecord writes a WAL record and assigns it a monotonic LSN. Returns the assigned LSN.
func (*WALFile) SetNextLSN ¶
SetNextLSN allows recovery to set the LSN counter.
type WALInfo ¶
type WALInfo struct {
PageSize int
Records int
MinLSN LSN
MaxLSN LSN
TxCount int
Committed int
Aborted int
PageImages int
}
WALInfo holds information about a WAL file.
func InspectWAL ¶
InspectWAL reads and summarises a WAL file.
type WALRecord ¶
type WALRecord struct {
Type WALRecordType
LSN LSN
TxID TxID
PageID PageID
Data []byte // full page image for PAGE_IMAGE, nil otherwise
}
WALRecord is an in-memory representation of a WAL record.
func ReadAllRecords ¶
ReadAllRecords reads all WAL records from the file (after the header). Partial/corrupt records at the tail are silently ignored (crash truncation).
type WALRecordType ¶
type WALRecordType uint8
WALRecordType identifies the kind of WAL record.
const ( WALRecordBegin WALRecordType = 0x01 WALRecordPageImage WALRecordType = 0x02 WALRecordCommit WALRecordType = 0x03 WALRecordAbort WALRecordType = 0x04 WALRecordCheckpoint WALRecordType = 0x05 )
func (WALRecordType) String ¶
func (rt WALRecordType) String() string