| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- package core
- import (
- "unsafe"
- "git.ycl.pub/yl32940/TsDB2/DB/core/page"
- "git.ycl.pub/yl32940/TsDB2/DB/core/types"
- "git.ycl.pub/yl32940/TsDB2/DB/core/util"
- )
- // Truncate 清空一张表的全部行与索引内容, 但保留表的身份 (TableInfo 槽位)、schema
- // (Field / 版本) 与索引【定义】(名字 / 类型 / 分量 / 唯一标志) —— 清空后表回到
- // "刚建表、零行、索引为空 (EntryPage=0, 首次插入自动开根)" 的状态。
- //
- // 与 Drop + Create 的区别: Drop 删掉 TableInfo 与索引定义, 再 Create 得到的是另一张
- // 表 (二级索引全部丢失, 需调用方逐个重建); Truncate 原地复用槽位与定义。
- func (t *TablePage) Truncate(name string) error {
- pageID := t.hdr.PageID
- for pageID != 0 {
- buf := make([]byte, PageSize)
- if err := t.handle.ReadPage(pageID, buf); err != nil {
- return err
- }
- hdr := page.HdrOf(buf)
- offset := t.hdrSize + schemaExtHeadSize
- for offset+t.tableInfoSize <= hdr.UseSize {
- info := (*TableInfo)(unsafe.Pointer(&buf[offset]))
- recSize := t.tableInfoSize + uint32(info.FieldAllocateSize)
- if util.BytesToString(info.Name[:]) == name && !util.IsHighBitSet(info.Flag) {
- var entry *Table
- for _, tb := range t.tables {
- if util.BytesToString(tb.info.Name[:]) == name && !util.IsHighBitSet(tb.info.Flag) {
- entry = tb
- break
- }
- }
- if entry == nil {
- return ErrTableNotFound
- }
- return entry.truncateContents(info)
- }
- offset += recSize
- }
- pageID = hdr.NextPage
- }
- return ErrTableNotFound
- }
- // truncateContents 释放本表全部数据 / 索引 / 堆页, 重建空 Data / Heap 首页, 并把每个
- // 索引的 EntryPage 置 0。页回收一律走 DeferFree / DeferFreeChain (提交成功后才真正还链)。
- //
- // 三类回收互不重叠:
- // - releaseIndexPages 收【索引自身页】: B+ 树节点 / 哈希桶链 / 时序扁平链 + TimeDataPage;
- // - FirstData 链收行存储 DataPage (OLTP 主键表) 或建表时的空 DataIndex 页 (时序表);
- // - FirstHeap 链收堆溢出页。
- func (table *Table) truncateContents(info *TableInfo) error {
- // 本地去重: 兜底防止任何两个枚举器碰到同一页 (重复还链会让空闲链自环)。
- seen := make(map[uint32]bool)
- deferFree := func(pid uint32) {
- if pid == 0 || seen[pid] {
- return
- }
- seen[pid] = true
- table.deferFreePage(pid)
- }
- // 1. 释放行持有的 blob 链 (必须在 DataPage 回收前, 行内容仍可读)。
- hasBlob := false
- for _, f := range table.Field {
- if types.IsBlob(f.FieldType) {
- hasBlob = true
- break
- }
- }
- if hasBlob {
- walked := make(map[uint32]bool)
- for pid := info.FirstData; pid != 0 && !walked[pid]; {
- walked[pid] = true
- dbuf := make([]byte, PageSize)
- if err := table.handle.ReadPage(pid, dbuf); err != nil {
- break
- }
- releaseBlobRefsInDataPage(table, dbuf)
- pid = page.HdrOf(dbuf).NextPage
- }
- }
- // 2. 逐索引释放内容并把 EntryPage 置 0 (空索引: 首次插入自动开根)。
- // 注意 releaseIndexPages 内部走 table.deferFreePage; 它收的索引页与下面的
- // Data / Heap 链不重叠 (见函数头说明)。
- im := table.indexMeta
- for i := range im.metas {
- m := &im.metas[i]
- if m.Flag&0x80 != 0 || m.EntryPage == 0 {
- continue
- }
- itype, root := m.IndexType, m.EntryPage
- releaseIndexPages(table, itype, root)
- m.EntryPage = 0
- im.saveMeta(m)
- }
- im.InvalidateRowCache()
- // 3. 释放数据页链 (无链长字段, 逐页)。
- walked := make(map[uint32]bool)
- for pid := info.FirstData; pid != 0 && !walked[pid]; {
- walked[pid] = true
- dbuf := make([]byte, PageSize)
- if err := table.handle.ReadPage(pid, dbuf); err != nil {
- break
- }
- next := page.HdrOf(dbuf).NextPage
- deferFree(pid)
- pid = next
- }
- // 4. 释放堆链 (首尾 + 链长已知, 整链 O(1) 延迟回收; 退化时逐页)。
- if info.FirstHeap != 0 && info.LastHeap != 0 && info.HeapPageCount > 0 && table.handle.DeferFreeChain != nil {
- table.handle.DeferFreeChain(info.FirstHeap, info.LastHeap, info.HeapPageCount)
- } else {
- hw := make(map[uint32]bool)
- for pid := info.FirstHeap; pid != 0 && !hw[pid]; {
- hw[pid] = true
- hbuf := make([]byte, PageSize)
- if err := table.handle.ReadPage(pid, hbuf); err != nil {
- break
- }
- next := page.HdrOf(hbuf).NextPage
- deferFree(pid)
- pid = next
- }
- }
- // 5. 重建空 Data / Heap 首页 (与 Create 的初始布局一致)。
- baseHdr := uint32(unsafe.Sizeof(page.PageHdr{}))
- dataPID := table.handle.NewPage()
- dbuf := make([]byte, PageSize)
- dHdr := page.HdrOf(dbuf)
- dHdr.Type = page.DataIndex
- dHdr.PageID = dataPID
- dHdr.UseSize = baseHdr
- if err := table.handle.SavePage(dataPID, dbuf); err != nil {
- return err
- }
- info.FirstData = dataPID
- info.LastData = dataPID
- heapPID := table.handle.NewPage()
- hbuf := make([]byte, PageSize)
- hHdr := page.HdrOf(hbuf)
- hHdr.Type = page.HeapIndex
- hHdr.PageID = heapPID
- hHdr.UseSize = baseHdr
- if err := table.handle.SavePage(heapPID, hbuf); err != nil {
- return err
- }
- info.FirstHeap = heapPID
- info.LastHeap = heapPID
- info.HeapPageCount = 1
- // 6. 行数清零并保存表信息。
- info.TotalRows = 0
- table.saveTableInfo()
- return nil
- }
|