table_truncate.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package core
  2. import (
  3. "unsafe"
  4. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  5. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  6. "git.ycl.pub/yl32940/TsDB2/DB/core/util"
  7. )
  8. // Truncate 清空一张表的全部行与索引内容, 但保留表的身份 (TableInfo 槽位)、schema
  9. // (Field / 版本) 与索引【定义】(名字 / 类型 / 分量 / 唯一标志) —— 清空后表回到
  10. // "刚建表、零行、索引为空 (EntryPage=0, 首次插入自动开根)" 的状态。
  11. //
  12. // 与 Drop + Create 的区别: Drop 删掉 TableInfo 与索引定义, 再 Create 得到的是另一张
  13. // 表 (二级索引全部丢失, 需调用方逐个重建); Truncate 原地复用槽位与定义。
  14. func (t *TablePage) Truncate(name string) error {
  15. pageID := t.hdr.PageID
  16. for pageID != 0 {
  17. buf := make([]byte, PageSize)
  18. if err := t.handle.ReadPage(pageID, buf); err != nil {
  19. return err
  20. }
  21. hdr := page.HdrOf(buf)
  22. offset := t.hdrSize + schemaExtHeadSize
  23. for offset+t.tableInfoSize <= hdr.UseSize {
  24. info := (*TableInfo)(unsafe.Pointer(&buf[offset]))
  25. recSize := t.tableInfoSize + uint32(info.FieldAllocateSize)
  26. if util.BytesToString(info.Name[:]) == name && !util.IsHighBitSet(info.Flag) {
  27. var entry *Table
  28. for _, tb := range t.tables {
  29. if util.BytesToString(tb.info.Name[:]) == name && !util.IsHighBitSet(tb.info.Flag) {
  30. entry = tb
  31. break
  32. }
  33. }
  34. if entry == nil {
  35. return ErrTableNotFound
  36. }
  37. return entry.truncateContents(info)
  38. }
  39. offset += recSize
  40. }
  41. pageID = hdr.NextPage
  42. }
  43. return ErrTableNotFound
  44. }
  45. // truncateContents 释放本表全部数据 / 索引 / 堆页, 重建空 Data / Heap 首页, 并把每个
  46. // 索引的 EntryPage 置 0。页回收一律走 DeferFree / DeferFreeChain (提交成功后才真正还链)。
  47. //
  48. // 三类回收互不重叠:
  49. // - releaseIndexPages 收【索引自身页】: B+ 树节点 / 哈希桶链 / 时序扁平链 + TimeDataPage;
  50. // - FirstData 链收行存储 DataPage (OLTP 主键表) 或建表时的空 DataIndex 页 (时序表);
  51. // - FirstHeap 链收堆溢出页。
  52. func (table *Table) truncateContents(info *TableInfo) error {
  53. // 本地去重: 兜底防止任何两个枚举器碰到同一页 (重复还链会让空闲链自环)。
  54. seen := make(map[uint32]bool)
  55. deferFree := func(pid uint32) {
  56. if pid == 0 || seen[pid] {
  57. return
  58. }
  59. seen[pid] = true
  60. table.deferFreePage(pid)
  61. }
  62. // 1. 释放行持有的 blob 链 (必须在 DataPage 回收前, 行内容仍可读)。
  63. hasBlob := false
  64. for _, f := range table.Field {
  65. if types.IsBlob(f.FieldType) {
  66. hasBlob = true
  67. break
  68. }
  69. }
  70. if hasBlob {
  71. walked := make(map[uint32]bool)
  72. for pid := info.FirstData; pid != 0 && !walked[pid]; {
  73. walked[pid] = true
  74. dbuf := make([]byte, PageSize)
  75. if err := table.handle.ReadPage(pid, dbuf); err != nil {
  76. break
  77. }
  78. releaseBlobRefsInDataPage(table, dbuf)
  79. pid = page.HdrOf(dbuf).NextPage
  80. }
  81. }
  82. // 2. 逐索引释放内容并把 EntryPage 置 0 (空索引: 首次插入自动开根)。
  83. // 注意 releaseIndexPages 内部走 table.deferFreePage; 它收的索引页与下面的
  84. // Data / Heap 链不重叠 (见函数头说明)。
  85. im := table.indexMeta
  86. for i := range im.metas {
  87. m := &im.metas[i]
  88. if m.Flag&0x80 != 0 || m.EntryPage == 0 {
  89. continue
  90. }
  91. itype, root := m.IndexType, m.EntryPage
  92. releaseIndexPages(table, itype, root)
  93. m.EntryPage = 0
  94. im.saveMeta(m)
  95. }
  96. im.InvalidateRowCache()
  97. // 3. 释放数据页链 (无链长字段, 逐页)。
  98. walked := make(map[uint32]bool)
  99. for pid := info.FirstData; pid != 0 && !walked[pid]; {
  100. walked[pid] = true
  101. dbuf := make([]byte, PageSize)
  102. if err := table.handle.ReadPage(pid, dbuf); err != nil {
  103. break
  104. }
  105. next := page.HdrOf(dbuf).NextPage
  106. deferFree(pid)
  107. pid = next
  108. }
  109. // 4. 释放堆链 (首尾 + 链长已知, 整链 O(1) 延迟回收; 退化时逐页)。
  110. if info.FirstHeap != 0 && info.LastHeap != 0 && info.HeapPageCount > 0 && table.handle.DeferFreeChain != nil {
  111. table.handle.DeferFreeChain(info.FirstHeap, info.LastHeap, info.HeapPageCount)
  112. } else {
  113. hw := make(map[uint32]bool)
  114. for pid := info.FirstHeap; pid != 0 && !hw[pid]; {
  115. hw[pid] = true
  116. hbuf := make([]byte, PageSize)
  117. if err := table.handle.ReadPage(pid, hbuf); err != nil {
  118. break
  119. }
  120. next := page.HdrOf(hbuf).NextPage
  121. deferFree(pid)
  122. pid = next
  123. }
  124. }
  125. // 5. 重建空 Data / Heap 首页 (与 Create 的初始布局一致)。
  126. baseHdr := uint32(unsafe.Sizeof(page.PageHdr{}))
  127. dataPID := table.handle.NewPage()
  128. dbuf := make([]byte, PageSize)
  129. dHdr := page.HdrOf(dbuf)
  130. dHdr.Type = page.DataIndex
  131. dHdr.PageID = dataPID
  132. dHdr.UseSize = baseHdr
  133. if err := table.handle.SavePage(dataPID, dbuf); err != nil {
  134. return err
  135. }
  136. info.FirstData = dataPID
  137. info.LastData = dataPID
  138. heapPID := table.handle.NewPage()
  139. hbuf := make([]byte, PageSize)
  140. hHdr := page.HdrOf(hbuf)
  141. hHdr.Type = page.HeapIndex
  142. hHdr.PageID = heapPID
  143. hHdr.UseSize = baseHdr
  144. if err := table.handle.SavePage(heapPID, hbuf); err != nil {
  145. return err
  146. }
  147. info.FirstHeap = heapPID
  148. info.LastHeap = heapPID
  149. info.HeapPageCount = 1
  150. // 6. 行数清零并保存表信息。
  151. info.TotalRows = 0
  152. table.saveTableInfo()
  153. return nil
  154. }