package index import ( "TsDB2/DB/core/page" ) // ======================== 页 I/O 回调 ======================== // GlobalBack 页 I/O 回调 (传递给索引规则,由外部实现) type GlobalBack struct { SavePage func(pagID uint32, data []byte) error GetNewPage func(pagID uint32) uint32 GetDataPage func(pagID uint32, data *[]byte) bool PoolGet func(size uint32) []byte PoolPut func([]byte) Handle page.HandleDb } // IEntry 索引条目操作接口(操作索引页内的条目字节切片) type IEntry interface { Compare(a, b []byte) int // 比较判断 Serialize(e IEntry) []byte // 序列化 Deserialize(buf []byte) IEntry // 反序列化 EntryLength() uint16 // 固定条目长度 Insert(Entries *[]byte, data []byte) error // 插入索引条目 Search(Entries []byte, key []byte) (uint32, bool) // 查找索引条目 DataEntry() IDateEntry // 获取数据页操作器 } // IDateEntry 数据页实际操作接口(增删改查真实行数据) // // 二级索引(Str/Compose): // - Insert: 在 IndexPage 条目中写入 [key + PK_ID(8B)] // - Search: 二分查找返回 PK_ID 的 8 字节编码(作为 rowData 返回) // - Delete: 标记删除条目 // // 主键索引(B+ 树)/ 时序索引: // - Insert: 在 DataPage 中写入行数据 // - Search: 找到 DataPage 返回行数据 // - Delete: 软删除行数据 type IDateEntry interface { // Insert 插入数据 // count: 当前页中已有的条目数量(由调用方从 IndexHead.Count 读取) // pkID: 主键ID(二级索引存入条目,B+树/时序忽略) // rowData: 序列化的行数据(B+树/时序写入 DataPage,二级索引忽略) Insert(handle page.HandleDb, indexEntries []byte, entryLen int, count int, key []byte, pkID uint64, rowData []byte) error // Search 查找数据 // 返回值: rowData 对于二级索引是 8 字节 PK_ID 编码,对于 B+树/时序是实际行数据 Search(handle page.HandleDb, indexEntries []byte, entryLen int, key []byte) ([]byte, bool) // Update 更新行数据(仅时序索引使用) Update(handle page.HandleDb, indexEntries []byte, entryLen int, key []byte, pkID uint64, rowData []byte) error // Delete 删除数据 Delete(handle page.HandleDb, indexEntries []byte, entryLen int, key []byte) error // CreateDataPage 创建一个新的数据页并写入第一行数据(仅时序索引需要) CreateDataPage(handle page.HandleDb, key []byte, rowData []byte) (uint32, error) } // IndexHandler 高层索引操作接口 // 每种索引类型实现此接口,封装所有存储细节(B+树路由、扁平链遍历等) // Table 层只需遍历 indexMeta.metas 并调用 handler 方法,无需关心索引内部实现 type IndexHandler interface { // Insert 向索引插入数据 // entryPage: 当前根页ID指针(B+树分裂时可能修改) // indexKey: 从行数据提取的索引键; pkID: 自增主键; rowData: 序列化行数据 // keyLen: 索引键字节长度 // 返回新的根页ID Insert(handle page.HandleDb, entryPage *uint32, indexKey []byte, pkID uint64, rowData []byte, keyLen int) (uint32, error) // Search 搜索索引 // 主键索引: 返回行数据(带pkID前缀), 调用方负责剥离 // 二级索引: 返回 8字节 BigEndian pkID Search(handle page.HandleDb, entryPage uint32, indexKey []byte, keyLen int) ([]byte, bool) // Delete 从索引删除条目 Delete(handle page.HandleDb, entryPage *uint32, indexKey []byte, keyLen int) error // Update 更新索引中的行数据 // oldKey: 旧索引键(定位条目); rowData: 新行数据; pkID: 主键ID Update(handle page.HandleDb, entryPage *uint32, oldKey []byte, rowData []byte, pkID uint64, keyLen int) error // GetDataPageIDs 返回所有数据页ID (按叶节点顺序, 用于全表扫描) GetDataPageIDs(handle page.HandleDb, entryPage uint32) []uint32 // ReadDataPageRows 读取数据页中所有未删除的行 (带 pkID(8B LE) 前缀) ReadDataPageRows(handle page.HandleDb, dpID uint32) [][]byte // RangeScan 范围扫描 // startKey/endKey: 范围边界 (nil 表示无下界/上界) // startInclusive/endInclusive: 是否包含边界 // reverse: true 时从右向左反向遍历 // callback: 每行回调, 返回 false 停止 // 主键/时序索引: row = [pkID(8B BE) + rowData] // 二级索引: row = 8B BigEndian pkID RangeScan(handle page.HandleDb, entryPage uint32, startKey, endKey []byte, startInclusive, endInclusive bool, reverse bool, callback func(row []byte) bool) error } type ComposeType struct { IndexEntry map[page.IndexType]IEntry IndexHandlers map[page.IndexType]IndexHandler } func (com *ComposeType) register(indexType page.IndexType, entry IEntry) { com.IndexEntry[indexType] = entry } func (com *ComposeType) RegisterHandler(indexType page.IndexType, handler IndexHandler) { com.IndexHandlers[indexType] = handler } func (com *ComposeType) GetEntryHandler(t page.IndexType) (IEntry, bool) { if entry, ok := com.IndexEntry[t]; ok { return entry, true } return nil, false } func (com *ComposeType) GetHandler(t page.IndexType) (IndexHandler, bool) { if h, ok := com.IndexHandlers[t]; ok { return h, true } return nil, false } var Compose = ComposeType{ IndexEntry: make(map[page.IndexType]IEntry), IndexHandlers: make(map[page.IndexType]IndexHandler), } func init() { Compose.register(page.IndexTypeTime, &IndexTimeEntry{}) Compose.register(page.IndexTypeCompose, &ComposeEntry{}) // 向后兼容:旧数据库中 IndexTypeStr=3 的索引条目用 ComposeEntry 处理 Compose.register(page.IndexTypeStr, &ComposeEntry{}) }