| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 |
- package core
- import (
- "TsDB2/DB/core/index"
- "TsDB2/DB/core/index/btree"
- "TsDB2/DB/core/index/datapage"
- "TsDB2/DB/core/page"
- "TsDB2/DB/core/types"
- "TsDB2/DB/core/util"
- "encoding/binary"
- "unsafe"
- )
- // 🔥 最终对接版:只存索引定义 + 指向你的 IndexPage
- type IndexMeta struct {
- IndexName [TableNameSize]byte
- IndexType page.IndexType
- FieldCnt byte // 列的数量
- FieldIDs [16]byte // 列
- EntryPage uint32 // ✅ 你的 IndexPage 首页ID
- PrevMetaID uint32 // 上一个
- NextMetaID uint32 // 下一个
- Flag byte // 标志位
- }
- type IndexMetaPage struct {
- hdr page.PageHdr
- metas []IndexMeta
- handle page.HandleDb
- hdrSize uint32
- metaSize uint32
- lastPageID uint32
- primaryLink map[string]string // 二级索引名 → 一级索引名
- primaryDPs []uint32 // 主键 B+ 树所有 DataPage ID 缓存
- }
- // 初始化
- func (im *IndexMetaPage) Init(pageID uint32) {
- im.hdrSize = uint32(unsafe.Sizeof(page.PageHdr{}))
- im.metaSize = uint32(unsafe.Sizeof(IndexMeta{}))
- im.metas = make([]IndexMeta, 0)
- im.hdr.PageID = pageID
- im.hdr.Type = page.IndexMetaIndex
- im.hdr.NextPage = 0
- if pageID == 0 {
- return
- }
- for pageID != 0 {
- im.lastPageID = pageID
- buf := make([]byte, PageSize)
- if err := im.handle.ReadPage(pageID, buf); err != nil {
- break
- }
- hdr := (*page.PageHdr)(unsafe.Pointer(&buf[0]))
- offset := im.hdrSize
- for offset+im.metaSize <= hdr.UseSize {
- m := (*IndexMeta)(unsafe.Pointer(&buf[offset]))
- if m.Flag&0x80 == 0 {
- im.metas = append(im.metas, *m)
- }
- offset += im.metaSize
- }
- pageID = hdr.NextPage
- }
- }
- // PageID 返回索引元数据页 ID (调试用)
- func (im *IndexMetaPage) PageID() uint32 { return im.hdr.PageID }
- // HdrSize 返回页头大小 (调试用)
- func (im *IndexMetaPage) HdrSize() uint32 { return im.hdrSize }
- // 创建索引(纯元数据,不碰IndexPage)
- func (im *IndexMetaPage) CreateIndex(indexName string, itype page.IndexType, fieldIDs []byte) (*IndexMeta, error) {
- fc := byte(len(fieldIDs))
- if fc == 0 || fc > 16 {
- return nil, ErrIndexFieldCount
- }
- for _, m := range im.metas {
- if util.BytesToString(m.IndexName[:]) == indexName && m.Flag&0x80 == 0 {
- return nil, ErrIndexExists
- }
- }
- var m IndexMeta
- copy(m.IndexName[:], indexName)
- m.IndexType = itype
- m.FieldCnt = fc
- copy(m.FieldIDs[:fc], fieldIDs)
- m.Flag = 0
- m.EntryPage = 0
- var buf []byte
- var hdr *page.PageHdr
- if im.hdr.PageID == 0 {
- pid := im.handle.NewPage()
- buf, hdr = im.InitHeader(pid)
- im.hdr.PageID = pid
- im.lastPageID = pid
- } else {
- buf = make([]byte, PageSize)
- im.handle.ReadPage(im.lastPageID, buf)
- hdr = (*page.PageHdr)(unsafe.Pointer(&buf[0]))
- }
- offset := hdr.UseSize
- if offset+im.metaSize > PageSize {
- newID := im.handle.NewPage()
- newBuf, newHdr := im.InitHeader(newID)
- hdr.NextPage = newID
- im.handle.SavePage(im.lastPageID, buf) // 保存下当前页 不然就被新开的页替换了
- im.lastPageID = newID
- buf = newBuf
- hdr = newHdr
- offset = im.hdrSize
- }
- dst := (*IndexMeta)(unsafe.Pointer(&buf[offset]))
- *dst = m
- hdr.UseSize = offset + im.metaSize
- im.handle.SavePage(im.lastPageID, buf)
- im.metas = append(im.metas, m)
- return &m, nil
- }
- // 查找单个索引
- func (im *IndexMetaPage) FindIndex(idxName string) (*IndexMeta, bool) {
- for i := range im.metas {
- m := &im.metas[i]
- if util.BytesToString(m.IndexName[:]) == idxName && m.Flag&0x80 == 0 {
- return m, true
- }
- }
- return nil, false
- }
- // 删除索引
- func (im *IndexMetaPage) DropIndex(idxName string) error {
- cur := im.hdr.PageID
- for cur != 0 {
- buf := make([]byte, PageSize)
- im.handle.ReadPage(cur, buf)
- hdr := (*page.PageHdr)(unsafe.Pointer(&buf[0]))
- offset := im.hdrSize
- for offset+im.metaSize <= hdr.UseSize {
- m := (*IndexMeta)(unsafe.Pointer(&buf[offset]))
- if util.BytesToString(m.IndexName[:]) == idxName && m.Flag&0x80 == 0 {
- m.Flag |= 0x80
- im.handle.SavePage(cur, buf)
- for i := range im.metas {
- if util.BytesToString(im.metas[i].IndexName[:]) == idxName {
- im.metas = append(im.metas[:i], im.metas[i+1:]...)
- break
- }
- }
- return nil
- }
- offset += im.metaSize
- }
- cur = hdr.NextPage
- }
- return ErrIndexNotFound
- }
- func (im *IndexMetaPage) InitHeader(pageID uint32) ([]byte, *page.PageHdr) {
- buf := make([]byte, PageSize)
- hdr := (*page.PageHdr)(unsafe.Pointer(&buf[0]))
- hdr.Type = page.IndexMetaIndex
- hdr.PageID = pageID
- hdr.UseSize = uint32(unsafe.Sizeof(*hdr))
- im.handle.SavePage(pageID, buf)
- return buf, hdr
- }
- // ======================== 高层索引操作 ========================
- func (im *IndexMetaPage) buildPrimaryLink() {
- im.primaryLink = make(map[string]string)
- var primaryName string
- for _, m := range im.metas {
- if m.Flag&0x80 != 0 {
- continue
- }
- name := util.BytesToString(m.IndexName[:])
- if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
- primaryName = name
- } else {
- if primaryName != "" {
- im.primaryLink[name] = primaryName
- }
- }
- }
- }
- func (im *IndexMetaPage) findIndexMetaByName(name string) *IndexMeta {
- for i := range im.metas {
- if util.BytesToString(im.metas[i].IndexName[:]) == name && im.metas[i].Flag&0x80 == 0 {
- return &im.metas[i]
- }
- }
- return nil
- }
- func (im *IndexMetaPage) getPrimaryFor(secName string) *IndexMeta {
- if im.primaryLink == nil {
- im.buildPrimaryLink()
- }
- if primName, ok := im.primaryLink[secName]; ok {
- return im.findIndexMetaByName(primName)
- }
- return nil
- }
- func (im *IndexMetaPage) saveMeta(m *IndexMeta) {
- cur := im.hdr.PageID
- for cur != 0 {
- buf := make([]byte, PageSize)
- im.handle.ReadPage(cur, buf)
- hdr := (*page.PageHdr)(unsafe.Pointer(&buf[0]))
- offset := im.hdrSize
- for offset+im.metaSize <= hdr.UseSize {
- dst := (*IndexMeta)(unsafe.Pointer(&buf[offset]))
- if util.BytesToString(dst.IndexName[:]) == util.BytesToString(m.IndexName[:]) {
- *dst = *m
- im.handle.SavePage(cur, buf)
- return
- }
- offset += im.metaSize
- }
- cur = hdr.NextPage
- }
- }
- // extractIndexKey 从行数据中提取索引键 (版本感知)
- // rowData 格式: [schemaVer(1B)] [field_data...]
- func extractIndexKey(rowData []byte, meta *IndexMeta, fields []types.FieldInfo, activeVer byte, versions []SchemaVersion) []byte {
- if meta.FieldCnt == 0 || len(rowData) < 1 {
- return nil
- }
- fieldIdx := int(meta.FieldIDs[0])
- // 从 rowData 读取该行的 schema 版本
- ver := rowData[0]
- var sv *SchemaVersion
- for i := range versions {
- if versions[i].Version == ver {
- sv = &versions[i]
- break
- }
- }
- if sv == nil {
- return nil
- }
- // 按该版本的字段顺序计算偏移
- off := 1 // 跳过 schemaVer
- for _, idx := range sv.FieldIndices {
- if int(idx) == fieldIdx {
- keyLen := int(getFieldSize(fields[fieldIdx].FieldType))
- if off+keyLen > len(rowData) {
- return nil
- }
- key := make([]byte, keyLen)
- copy(key, rowData[off:off+keyLen])
- return key
- }
- off += int(getFieldSize(fields[idx].FieldType))
- }
- return nil
- }
- // InsertRow 插入行到所有索引
- func (im *IndexMetaPage) InsertRow(fields []types.FieldInfo, pkID uint64, rowData []byte, activeVer byte, versions []SchemaVersion) error {
- im.primaryDPs = nil // 插入可能创建新 DataPage, 失效缓存
- for i := range im.metas {
- m := &im.metas[i]
- if m.Flag&0x80 != 0 {
- continue
- }
- handler, ok := index.Compose.GetHandler(m.IndexType)
- if !ok {
- continue
- }
- indexKey := extractIndexKey(rowData, m, fields, activeVer, versions)
- if indexKey == nil {
- continue
- }
- entryPage := m.EntryPage
- _, err := handler.Insert(im.handle, &entryPage, indexKey, pkID, rowData, len(indexKey))
- if err != nil {
- return err
- }
- if entryPage != m.EntryPage {
- m.EntryPage = entryPage
- im.saveMeta(m)
- }
- }
- return nil
- }
- // SearchByIndex 通过索引搜索
- func (im *IndexMetaPage) SearchByIndex(idxName string, indexKey []byte, fields []types.FieldInfo) ([]byte, bool) {
- meta := im.findIndexMetaByName(idxName)
- if meta == nil {
- return nil, false
- }
- handler, ok := index.Compose.GetHandler(meta.IndexType)
- if !ok {
- return nil, false
- }
- result, found := handler.Search(im.handle, meta.EntryPage, indexKey, len(indexKey))
- if !found {
- return nil, false
- }
- if meta.IndexType == page.IndexTypePrimary || meta.IndexType == page.IndexTypeTime {
- return result, true
- }
- // 二级索引: result 是 8B BigEndian pkID, 需要回查主键
- pkID := binary.BigEndian.Uint64(result)
- return im.ResolveByPK(pkID, fields)
- }
- // ResolveByPK 通过主键ID (pkID) 查找行数据
- // 注意: pkID 是自增计数器, 不一定等于主键索引字段值
- // 因此不能直接用 pkID 搜索主键 B+ 树 (B+ 树 key = 索引字段值)
- // 必须扫描所有 DataPage 按存储的 pkID 匹配
- func (im *IndexMetaPage) ResolveByPK(pkID uint64, fields []types.FieldInfo) ([]byte, bool) {
- pkKeyBE := make([]byte, 8)
- binary.BigEndian.PutUint64(pkKeyBE, pkID)
- for _, m := range im.metas {
- if m.Flag&0x80 != 0 {
- continue
- }
- if m.IndexType == page.IndexTypePrimary {
- // 主键 B+ 树: DataPage 按 pkID 顺序, 用二分查找定位目标页
- if im.primaryDPs == nil {
- im.primaryDPs = btree.GetDataPageIDs(im.handle, m.EntryPage)
- }
- targetPK := binary.BigEndian.Uint64(pkKeyBE)
- // 二分查找: 找第一个 MaxPK >= targetPK 的 DataPage
- left, right := 0, len(im.primaryDPs)-1
- for left <= right {
- mid := (left + right) / 2
- dp, err := datapage.Load(im.handle, im.primaryDPs[mid])
- if err != nil || dp.Count() == 0 {
- // 空页, 跳过 (但不影响二分逻辑)
- left = mid + 1
- continue
- }
- if dp.MaxPK() < targetPK {
- // 当前页的最大 pkID < 目标, 目标在右侧
- left = mid + 1
- } else {
- // 当前页的最大 pkID >= 目标, 可能是目标页, 继续向左找
- right = mid - 1
- }
- }
- // left 指向第一个可能包含目标的 DataPage
- if left >= 0 && left < len(im.primaryDPs) {
- dp, err := datapage.Load(im.handle, im.primaryDPs[left])
- if err == nil && dp.Count() > 0 {
- if targetPK >= dp.MinPK() && targetPK <= dp.MaxPK() {
- if storedRow, ok := dp.FindRowByPK(pkKeyBE); ok {
- return storedRow, true
- }
- }
- }
- }
- return nil, false
- }
- if m.IndexType == page.IndexTypeTime {
- // 时序索引: 遍历条目链找到 DataPage
- curPageID := m.EntryPage
- for curPageID != 0 {
- buf := make([]byte, PageSize)
- if err := im.handle.ReadPage(curPageID, buf); err != nil {
- break
- }
- hdr := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
- entryLen := int(hdr.EntryLen)
- count := int(hdr.Count)
- entries := buf[page.IndexHeadSize : page.IndexHeadSize+count*entryLen]
- for i := 0; i < count; i++ {
- dpID := binary.BigEndian.Uint32(entries[i*entryLen+entryLen-4:])
- if dpID != 0 {
- dp, err := datapage.Load(im.handle, dpID)
- if err == nil {
- if storedRow, ok := dp.FindRowByPK(pkKeyBE); ok {
- return storedRow, true
- }
- }
- }
- }
- curPageID = hdr.Hdr.NextPage
- }
- return nil, false
- }
- }
- return nil, false
- }
- // DeleteByKey 通过索引删除行(级联处理二级索引)
- func (im *IndexMetaPage) DeleteByKey(idxName string, indexKey []byte, fields []types.FieldInfo, activeVer byte, versions []SchemaVersion) error {
- im.primaryDPs = nil // 删除可能释放 DataPage, 失效缓存
- if im.primaryLink == nil {
- im.buildPrimaryLink()
- }
- meta := im.findIndexMetaByName(idxName)
- if meta == nil {
- return page.ErrNotFound
- }
- handler, ok := index.Compose.GetHandler(meta.IndexType)
- if !ok {
- return page.ErrNotFound
- }
- if meta.IndexType == page.IndexTypePrimary || meta.IndexType == page.IndexTypeTime {
- // 主键/时序索引: 先读取行数据用于级联删除二级索引
- var rowData []byte
- if meta.IndexType == page.IndexTypePrimary {
- if data, found := handler.Search(im.handle, meta.EntryPage, indexKey, len(indexKey)); found {
- rowData = data
- }
- }
- entryPage := meta.EntryPage
- err := handler.Delete(im.handle, &entryPage, indexKey, len(indexKey))
- if entryPage != meta.EntryPage {
- meta.EntryPage = entryPage
- im.saveMeta(meta)
- }
- // 级联删除二级索引
- if rowData != nil {
- // PrimaryHandler 返回的 rowData 包含 8B LE pkID 前缀, 需要剥离
- origRow := rowData
- if len(origRow) > 8 {
- origRow = origRow[8:]
- }
- primName := util.BytesToString(meta.IndexName[:])
- for i := range im.metas {
- m := &im.metas[i]
- if m.Flag&0x80 != 0 {
- continue
- }
- secName := util.BytesToString(m.IndexName[:])
- if linkedPrim, ok := im.primaryLink[secName]; ok && linkedPrim == primName {
- secHandler, ok := index.Compose.GetHandler(m.IndexType)
- if ok {
- secKey := extractIndexKey(origRow, m, fields, activeVer, versions)
- if secKey != nil {
- secEntryPage := m.EntryPage
- secHandler.Delete(im.handle, &secEntryPage, secKey, len(secKey))
- if secEntryPage != m.EntryPage {
- m.EntryPage = secEntryPage
- im.saveMeta(m)
- }
- }
- }
- }
- }
- }
- return err
- }
- // 二级索引: 先查 pkID, 删主键, 再删二级
- pkIDBuf, found := handler.Search(im.handle, meta.EntryPage, indexKey, len(indexKey))
- if !found {
- return page.ErrNotFound
- }
- pkID := binary.BigEndian.Uint64(pkIDBuf)
- // 找主键索引删除
- primMeta := im.getPrimaryFor(idxName)
- if primMeta != nil {
- primHandler, ok := index.Compose.GetHandler(primMeta.IndexType)
- if ok {
- pkKey := make([]byte, 8)
- binary.BigEndian.PutUint64(pkKey, pkID)
- entryPage := primMeta.EntryPage
- switch primMeta.IndexType {
- case page.IndexTypePrimary:
- // 主键 B+ 树: 用 DeleteByPK 按 pkID 匹配
- primHandler.(*index.PrimaryHandler).DeleteByPK(im.handle, &entryPage, pkID)
- case page.IndexTypeTime:
- // 时序索引: 遍历所有条目, 从 DataPage 按 pkID 删除
- curPageID := entryPage
- for curPageID != 0 {
- buf := make([]byte, page.IndexPageSize)
- if err := im.handle.ReadPage(curPageID, buf); err != nil {
- break
- }
- hdr := (*page.IndexHead)(unsafe.Pointer(&buf[0]))
- entryLen := int(hdr.EntryLen)
- count := int(hdr.Count)
- entries := buf[page.IndexHeadSize : page.IndexHeadSize+count*entryLen]
- for i := 0; i < count; i++ {
- dpID := binary.BigEndian.Uint32(entries[i*entryLen+entryLen-4:])
- if dpID != 0 {
- if dp, err := datapage.Load(im.handle, dpID); err == nil {
- if dp.DeleteByPK(pkKey) {
- if dp.AllDeleted() {
- if im.handle.FreePage != nil {
- im.handle.FreePage(dpID)
- }
- } else {
- dp.Save()
- }
- goto timeDeleteDone
- }
- }
- }
- }
- curPageID = hdr.Hdr.NextPage
- }
- timeDeleteDone:
- }
- if entryPage != primMeta.EntryPage {
- primMeta.EntryPage = entryPage
- im.saveMeta(primMeta)
- }
- }
- }
- // 删二级索引条目
- entryPage := meta.EntryPage
- err := handler.Delete(im.handle, &entryPage, indexKey, len(indexKey))
- if entryPage != meta.EntryPage {
- meta.EntryPage = entryPage
- im.saveMeta(meta)
- }
- return err
- }
- // UpdateByKey 通过索引更新行(级联处理二级索引)
- func (im *IndexMetaPage) UpdateByKey(idxName string, indexKey []byte, newRowData []byte, pkID uint64, fields []types.FieldInfo, activeVer byte, versions []SchemaVersion) error {
- if im.primaryLink == nil {
- im.buildPrimaryLink()
- }
- // 更新主键索引中的行数据
- for i := range im.metas {
- m := &im.metas[i]
- if m.Flag&0x80 != 0 {
- continue
- }
- if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
- handler, ok := index.Compose.GetHandler(m.IndexType)
- if ok {
- // 主键/时序索引始终使用 pkID(8B BE) 作为 B+ 树 key
- primKey := make([]byte, 8)
- binary.BigEndian.PutUint64(primKey, pkID)
- entryPage := m.EntryPage
- handler.Update(im.handle, &entryPage, primKey, newRowData, pkID, 8)
- if entryPage != m.EntryPage {
- m.EntryPage = entryPage
- im.saveMeta(m)
- }
- }
- break
- }
- }
- // 更新二级索引(如果索引键变了需要删旧插新)
- for i := range im.metas {
- m := &im.metas[i]
- if m.Flag&0x80 != 0 {
- continue
- }
- if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
- continue
- }
- handler, ok := index.Compose.GetHandler(m.IndexType)
- if !ok {
- continue
- }
- newKey := extractIndexKey(newRowData, m, fields, activeVer, versions)
- if newKey == nil {
- continue
- }
- entryPage := m.EntryPage
- handler.Update(im.handle, &entryPage, newKey, newRowData, pkID, len(newKey))
- if entryPage != m.EntryPage {
- m.EntryPage = entryPage
- im.saveMeta(m)
- }
- }
- return nil
- }
|