|
|
@@ -1,6 +1,7 @@
|
|
|
package core
|
|
|
|
|
|
import (
|
|
|
+ "TsDB2/DB/core/index/btree"
|
|
|
"TsDB2/DB/core/page"
|
|
|
"TsDB2/DB/core/types"
|
|
|
"TsDB2/DB/core/util"
|
|
|
@@ -249,7 +250,7 @@ func (t *TablePage) Create(name string, fields []types.FieldInfo) (*Table, error
|
|
|
}
|
|
|
|
|
|
// ===========================================================================
|
|
|
-// ✅ Drop 软删除
|
|
|
+// ✅ Drop 删除表并回收所有页
|
|
|
// ===========================================================================
|
|
|
func (t *TablePage) Drop(name string) error {
|
|
|
pageID := t.hdr.PageID
|
|
|
@@ -265,6 +266,17 @@ func (t *TablePage) Drop(name string) error {
|
|
|
for offset+t.tableInfoSize <= hdr.UseSize {
|
|
|
info := (*TableInfo)(unsafe.Pointer(&buf[offset]))
|
|
|
if util.BytesToString(info.Name[:]) == name && !util.IsHighBitSet(info.Flag) {
|
|
|
+ // 找到该表在内存中的 Table 对象,获取其索引元数据
|
|
|
+ var tableEntry *Table
|
|
|
+ for _, tb := range t.tables {
|
|
|
+ if util.BytesToString(tb.info.Name[:]) == name && !util.IsHighBitSet(tb.info.Flag) {
|
|
|
+ tableEntry = tb
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 回收该表所有页 (用该表自己的 indexMeta.metas,不遍历共享的 IndexMeta 页链)
|
|
|
+ t.recycleTablePages(info, tableEntry)
|
|
|
+ // 软删除标记
|
|
|
info.Flag = util.SetHighBit(info.Flag)
|
|
|
t.handle.SavePage(pageID, buf)
|
|
|
for i := range t.tables {
|
|
|
@@ -282,6 +294,67 @@ func (t *TablePage) Drop(name string) error {
|
|
|
return ErrTableNotFound
|
|
|
}
|
|
|
|
|
|
+// recycleTablePages 回收表的所有页 (B+树页、DataPage页、Heap页)
|
|
|
+// 注意: IndexMeta 页是所有表共享的,不能释放!
|
|
|
+func (t *TablePage) recycleTablePages(info *TableInfo, tableEntry *Table) {
|
|
|
+ if t.handle.FreePage == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ var pagesToFree []uint32
|
|
|
+
|
|
|
+ // 1. 用该表自己的 indexMeta.metas 收集 B+ 树页和 DataPage
|
|
|
+ // 不遍历共享的 IndexMeta 页链,避免误释放其他表的索引页
|
|
|
+ if tableEntry != nil && tableEntry.indexMeta != nil {
|
|
|
+ for i := range tableEntry.indexMeta.metas {
|
|
|
+ m := &tableEntry.indexMeta.metas[i]
|
|
|
+ if m.Flag&0x80 != 0 || m.EntryPage == 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 收集 B+ 树所有节点页 (内部节点+叶子节点)
|
|
|
+ btPages := btree.CollectAllPageIDs(t.handle, m.EntryPage)
|
|
|
+ pagesToFree = append(pagesToFree, btPages...)
|
|
|
+ // 只有主键/时序索引的叶子节点存 DataPage ID
|
|
|
+ // 二级索引叶子存的是 PKID(8B),不是 DataPage ID!
|
|
|
+ if m.IndexType == page.IndexTypePrimary || m.IndexType == page.IndexTypeTime {
|
|
|
+ dpIDs := btree.GetDataPageIDs(t.handle, m.EntryPage)
|
|
|
+ pagesToFree = append(pagesToFree, dpIDs...)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 收集 Heap 页链 (溢出页)
|
|
|
+ heapID := info.FirstHeap
|
|
|
+ for heapID != 0 {
|
|
|
+ buf := make([]byte, PageSize)
|
|
|
+ if err := t.handle.ReadPage(heapID, buf); err != nil {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ hdr := (*page.PageHdr)(unsafe.Pointer(&buf[0]))
|
|
|
+ nextHeap := hdr.NextPage
|
|
|
+ pagesToFree = append(pagesToFree, heapID)
|
|
|
+ heapID = nextHeap
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 收集 Overflow 页链
|
|
|
+ oflowID := info.FirstOverflow
|
|
|
+ for oflowID != 0 {
|
|
|
+ buf := make([]byte, PageSize)
|
|
|
+ if err := t.handle.ReadPage(oflowID, buf); err != nil {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ hdr := (*page.PageHdr)(unsafe.Pointer(&buf[0]))
|
|
|
+ nextOflow := hdr.NextPage
|
|
|
+ pagesToFree = append(pagesToFree, oflowID)
|
|
|
+ oflowID = nextOflow
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 统一释放所有页
|
|
|
+ for _, pid := range pagesToFree {
|
|
|
+ t.handle.FreePage(pid)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func (t *TablePage) Find(name string) (*Table, bool) {
|
|
|
for i := range t.tables {
|
|
|
tb := t.tables[i]
|