Table.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. package core
  2. import (
  3. "encoding/binary"
  4. "sync"
  5. "unsafe"
  6. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  7. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  8. "git.ycl.pub/yl32940/TsDB2/DB/core/util"
  9. )
  10. // SchemaFragHdr 共享扩展页里每个 schema 续块的头 (定长 8 字节, 紧随 payload)
  11. // 警告: 该结构体仅用于计算大小/表达语义, 写盘一律用 putSchemaFragHdr,
  12. // 不要做 `*(*SchemaFragHdr)(p) = v` 的结构体赋值。
  13. type SchemaFragHdr struct {
  14. NextPage uint32 // 下一个续块所在页 (0 = 本表续块链结束)
  15. NextOff uint16 // 下一个续块在页内的物理偏移
  16. DataLen uint16 // 本续块 payload 字节数
  17. }
  18. // schemaFragHdrSize schema 续块头的物理大小 (字节)
  19. const schemaFragHdrSize = 8
  20. // putSchemaFragHdr 写入续块头 (大端: NextPage 4B + NextOff 2B + DataLen 2B)
  21. func putSchemaFragHdr(buf []byte, off int, h SchemaFragHdr) {
  22. binary.BigEndian.PutUint32(buf[off:off+4], h.NextPage)
  23. binary.BigEndian.PutUint16(buf[off+4:off+6], h.NextOff)
  24. binary.BigEndian.PutUint16(buf[off+6:off+8], h.DataLen)
  25. }
  26. // readSchemaFragHdr 读取续块头
  27. func readSchemaFragHdr(buf []byte, off int) SchemaFragHdr {
  28. return SchemaFragHdr{
  29. NextPage: binary.BigEndian.Uint32(buf[off : off+4]),
  30. NextOff: binary.BigEndian.Uint16(buf[off+4 : off+6]),
  31. DataLen: binary.BigEndian.Uint16(buf[off+6 : off+8]),
  32. }
  33. }
  34. // SchemaVersion 表的一个 schema 版本定义
  35. type SchemaVersion struct {
  36. Version byte
  37. FieldIndices []byte // 每个字段在 masterFields 中的索引
  38. }
  39. type TableInfo struct {
  40. Name [TableNameSize]byte
  41. Flag byte // 表的状态是否被删除
  42. FieldCount byte // 当前字段的总数量(包含已删除,永不减少)byte=255上限
  43. ActiveFieldCount byte // 实际活跃字段数量(不包含已删除)
  44. _ byte // 1 显式对齐填充 (TotalRows 需要 8 字节对齐)
  45. // HeapPageCount 本表堆链 (FirstHeap→LastHeap) 上的页数。
  46. //
  47. // 塞进 TotalRows 前的对齐填充里 (那 4 字节本来就是编译器要插入的空洞), 不额外
  48. // 增加 TableInfo 的体积。
  49. // 用途: DropTable 把整条堆链 O(1) 拼进空闲链时, 不遍历也有链长可累加空闲页计数。
  50. HeapPageCount uint32 // 4 堆链页数
  51. TotalRows uint64 // 当前表行的总数量
  52. FirstHeap uint32 // 堆链首页
  53. LastHeap uint32 // 堆链尾页
  54. FirstData uint32 // 数据链首页
  55. LastData uint32 // 数据链尾
  56. RowSize uint16 // 单条记录大小(字节)
  57. FieldAllocateSize uint16 // 当前列总分配大小
  58. IndexCount byte // 这张表有几个索引
  59. FirstIndexID uint32 // 本表第一个索引的元数据位置
  60. LastIndexID uint32 // 本表最后一个索引的元数据位置
  61. // ExtRef schema 续块在「全库共享扩展页」中的位置
  62. // (复用原 FirstOverflow/LastOverflow 占用的 8 字节: ExtPage 落在 8 字节对齐处,
  63. // 尾部 2 字节显式填充, 使 TableInfo 整体大小为 8 的倍数)
  64. ExtPage uint32 // 4 首个续块所在扩展页 ID (0 = schema 全部内联, 无续块)
  65. ExtOff uint16 // 2 首个续块在页内的物理偏移
  66. _ uint16 // 2 显式填充, 使整体大小为 8 的倍数
  67. }
  68. // 编译期断言: 钉死 TableInfo 的盘上大小与字段布局。
  69. //
  70. // TableNameSize 变化会整体平移 Name 之后所有字段的偏移 —— analyzer 那套硬编码偏移
  71. // (analyzer/main.go 的 TI_*) 必须同步修改, 改漏了会静默读错数据。这里把大小钉死,
  72. // 让「改了布局却忘了改断言」直接编译失败。
  73. //
  74. // 布局 (TableNameSize=48) = 104 字节:
  75. //
  76. // Name[48] + Flag/FieldCount/ActiveFieldCount/pad(4) + HeapPageCount(4) + TotalRows(8)
  77. // + FirstHeap/LastHeap/FirstData/LastData(16) + RowSize/FieldAllocateSize(4)
  78. // + IndexCount/pad(4) + FirstIndexID/LastIndexID/ExtPage(12) + ExtOff/pad(4)
  79. var (
  80. _ [unsafe.Sizeof(TableInfo{}) - 104]struct{}
  81. _ [104 - unsafe.Sizeof(TableInfo{})]struct{}
  82. )
  83. // readSchema 读取一张表完整的 schema 定义
  84. // 数据来源: 表页内的固定内联块 (allocSize 字节) + 共享扩展页里的续块链
  85. // 新格式: [activeVer(1B)] [masterCount(1B)] [type+len+name]×masterCount [verCount(1B)] [verDefs...]
  86. func readSchema(handle page.HandleDb, pageID uint32, startOff uint16, allocSize uint16,
  87. extPage uint32, extOff uint16) (byte, []types.FieldInfo, []SchemaVersion) {
  88. // 1. 内联块 (表页内, 固定 allocSize 字节, 无跳转槽)
  89. var data []byte
  90. buf := make([]byte, PageSize)
  91. if err := handle.ReadPage(pageID, buf); err == nil {
  92. end := int(startOff) + int(allocSize)
  93. if end > len(buf) {
  94. end = len(buf)
  95. }
  96. if int(startOff) < end {
  97. data = append(data, buf[startOff:end]...)
  98. }
  99. }
  100. // 2. 续块链 (共享扩展页): [SchemaFragHdr 8B][payload]
  101. for pid, off := extPage, extOff; pid != 0; {
  102. fb := make([]byte, PageSize)
  103. if err := handle.ReadPage(pid, fb); err != nil {
  104. break
  105. }
  106. if int(off)+schemaFragHdrSize > len(fb) {
  107. break
  108. }
  109. frag := readSchemaFragHdr(fb, int(off))
  110. start := int(off) + schemaFragHdrSize
  111. end := start + int(frag.DataLen)
  112. if end > len(fb) {
  113. break
  114. }
  115. data = append(data, fb[start:end]...)
  116. pid, off = frag.NextPage, frag.NextOff
  117. }
  118. if len(data) < 2 {
  119. return 0, nil, nil
  120. }
  121. pos := 0
  122. activeVer := data[pos]
  123. pos++
  124. masterCount := int(data[pos])
  125. pos++
  126. // 读取 master fields: [type(1B)][flag(1B)][nameLen(1B)][name...]
  127. // flag 是列级标志 (删除/可空/唯一 + decimal 的 scale), 见 types.FieldInfo.Flag。
  128. // 语义变了但字节位置与顺序不变: 必须与 serializeSchema 严格对照。
  129. var masterFields []types.FieldInfo
  130. for i := 0; i < masterCount && pos+3 <= len(data); i++ {
  131. typ := types.FieldType(data[pos])
  132. flag := data[pos+1]
  133. nameLen := int(data[pos+2])
  134. pos += 3
  135. if pos+nameLen > len(data) {
  136. break
  137. }
  138. name := make([]byte, nameLen)
  139. copy(name, data[pos:pos+nameLen])
  140. pos += nameLen
  141. masterFields = append(masterFields, types.FieldInfo{FieldType: typ, FieldName: name, Flag: flag})
  142. }
  143. // 读取版本定义
  144. var versions []SchemaVersion
  145. if pos < len(data) {
  146. verCount := int(data[pos])
  147. pos++
  148. for i := 0; i < verCount && pos < len(data); i++ {
  149. verNo := data[pos]
  150. pos++
  151. if pos >= len(data) {
  152. break
  153. }
  154. fieldCount := int(data[pos])
  155. pos++
  156. indices := make([]byte, fieldCount)
  157. if pos+fieldCount <= len(data) {
  158. copy(indices, data[pos:pos+fieldCount])
  159. pos += fieldCount
  160. }
  161. versions = append(versions, SchemaVersion{Version: verNo, FieldIndices: indices})
  162. }
  163. }
  164. return activeVer, masterFields, versions
  165. }
  166. type Table struct {
  167. info *TableInfo
  168. Field []types.FieldInfo // master registry (所有字段,含已删除,删除标记见 types.FlagDeleted)
  169. // db 所属 DB (BlobRead/BlobFree 需要它取 file / pendingFreeChain).
  170. // 创建时由 db.tables.Create 注入, 运行时不变.
  171. db *DB
  172. handle page.HandleDb
  173. indexMeta *IndexMetaPage
  174. tablePageID uint32 // 表ID
  175. tableStartOffset uint32 // 表所在页的偏移
  176. // schema 续块所在的「全库共享扩展页」分配器 (由 TablePage 注入, 各表同一实例)
  177. schemaExt *SchemaExtAllocator
  178. // schema 版本管理
  179. activeVersion byte
  180. versions []SchemaVersion
  181. // 写操作通知回调 (防抖自动提交)
  182. onWrite func()
  183. // 自增主键游标 (内存态, 详见 AllocPKID/ensureNextPK):
  184. // 不能再现算 TotalRows+1 —— 删除会让行数回退, 编号随之回退并撞上存活的 pkID。
  185. nextPK uint64
  186. nextPKInit bool
  187. pendingPK uint64 // AllocPKID 刚预留、等待 InsertRowWithID 消费的号
  188. hasPendingPK bool
  189. // pkMu 保护自增主键游标的读-改-写 (ensureNextPK / nextPK++ / pendingPK 预留):
  190. // 多写者并发 AllocPKID 时, 无锁的 nextPK++ 会给两行发出同一个号 (重复主键)。
  191. pkMu sync.Mutex
  192. // fieldIdxCache: 列名 → table.Field 下标的惰性缓存。条件求值
  193. // (Eq/In/Gt... 的 Match) 每行每条件都查一次字段位置, 线性扫是 O(列数)/行;
  194. // 表字段在建模后基本不变, 缓存后 O(1)。Field 切片身份变化 (AddField 追加 /
  195. // ReloadInfos 整体换片) 时按首元素指针 + 长度自动重建, 无需手工失效。
  196. // fieldIdxMu 保护首次构建: 引擎允许并发读, 两个读 goroutine 可能同时建缓存。
  197. fieldIdxMu sync.Mutex
  198. fieldIdxCache map[string]int
  199. fieldIdxSrc []types.FieldInfo // 建缓存时的 Field 切片 (身份比对用)
  200. }
  201. // TotalRows 返回表的总行数
  202. func (table *Table) TotalRows() uint64 { return table.info.TotalRows }
  203. // TablePageID 返回表页 ID (调试用)
  204. func (table *Table) TablePageID() uint32 { return table.tablePageID }
  205. // TableStartOffset 返回表信息在页中的偏移 (调试用)
  206. func (table *Table) TableStartOffset() uint32 { return table.tableStartOffset }
  207. // IndexMeta 返回索引元数据页 (调试用)
  208. func (table *Table) IndexMeta() *IndexMetaPage { return table.indexMeta }
  209. // SetOnWrite 设置写操作通知回调 (防抖自动提交)
  210. func (table *Table) SetOnWrite(fn func()) { table.onWrite = fn }
  211. // findFieldIndex 查找字段在 master registry 中的索引
  212. //
  213. // 走 fieldIndexMap 的惰性缓存: Match 类条件是"每行每条件一次"的热路径, 线性扫
  214. // O(列数) 在宽表 + 全表扫描下会累积成可见开销。缓存按 Field 切片身份
  215. // (首元素指针 + 长度) 自动失效, AddField / ReloadInfos 后下一次调用即重建。
  216. func (table *Table) findFieldIndex(name string) int {
  217. if idx, ok := table.fieldIndexMap()[name]; ok {
  218. return idx
  219. }
  220. return -1
  221. }
  222. // fieldIndexMap 返回 (必要时重建) 列名 → Field 下标缓存。
  223. func (table *Table) fieldIndexMap() map[string]int {
  224. src := table.Field
  225. if table.fieldIdxCache != nil && len(table.fieldIdxSrc) == len(src) &&
  226. (len(src) == 0 || &table.fieldIdxSrc[0] == &src[0]) {
  227. return table.fieldIdxCache
  228. }
  229. table.fieldIdxMu.Lock()
  230. defer table.fieldIdxMu.Unlock()
  231. // 双检: 等待锁期间另一读线程可能已建好
  232. if table.fieldIdxCache != nil && len(table.fieldIdxSrc) == len(src) &&
  233. (len(src) == 0 || &table.fieldIdxSrc[0] == &src[0]) {
  234. return table.fieldIdxCache
  235. }
  236. m := make(map[string]int, len(src))
  237. for i := range src {
  238. m[string(src[i].FieldName)] = i
  239. }
  240. table.fieldIdxCache = m
  241. table.fieldIdxSrc = src
  242. return m
  243. }
  244. // fieldWidth 取某列的行内宽度 (字节) —— 引擎内所有行偏移计算的唯一入口。
  245. //
  246. // 为什么不能直接 types.GetSize(f.FieldType): 字符串列的行内宽度由【列自己声明】
  247. // (Flag 低 5 位的宽度码, 上限 types.MaxInlineColumnWidth), 只看类型会一律算成 256,
  248. // 于是该列之后的所有列都从错位偏移解码。这里统一经 FieldInfo.Width()。
  249. //
  250. // 返回 0 表示索引越界或列级参数非法 —— 调用方必须当错误处理: 0 宽度会让整行错位。
  251. func (table *Table) fieldWidth(masterIdx int) uint16 {
  252. if masterIdx < 0 || masterIdx >= len(table.Field) {
  253. return 0
  254. }
  255. return table.Field[masterIdx].Width()
  256. }
  257. func (table *Table) GetFieldCount() byte { return table.info.FieldCount }
  258. func (table *Table) GetActiveFieldCount() byte { return table.info.ActiveFieldCount }
  259. // GetActiveFieldNames 返回当前活跃版本中包含的所有字段名 (不含已删除)
  260. func (table *Table) GetActiveFieldNames() []string {
  261. sv := table.getVersion(table.activeVersion)
  262. if sv == nil {
  263. return nil
  264. }
  265. names := make([]string, 0, len(sv.FieldIndices))
  266. for _, idx := range sv.FieldIndices {
  267. if table.Field[idx].IsDropped() {
  268. continue // 跳过已删除
  269. }
  270. names = append(names, string(table.Field[idx].FieldName))
  271. }
  272. return names
  273. }
  274. // GetActiveFieldType 返回指定字段的 FieldType (基于 master registry)
  275. func (table *Table) GetActiveFieldType(name string) (types.FieldType, bool) {
  276. for _, f := range table.Field {
  277. if string(f.FieldName) == name && !f.IsDropped() {
  278. return f.FieldType, true
  279. }
  280. }
  281. return 0, false
  282. }
  283. // GetActiveFieldFlag 返回指定字段的列级标志 (含 Decimal 的 scale 与可空位)。
  284. //
  285. // 与 GetActiveFieldType 配对: 类型一致但 scale 不一致是"同列不同 scale"这种会被
  286. // 静默破坏保序前提的情况, 调用方 (ORM 重开表时) 必须能读到它来做一致性校验。
  287. func (table *Table) GetActiveFieldFlag(name string) (byte, bool) {
  288. for i := range table.Field {
  289. f := &table.Field[i]
  290. if string(f.FieldName) == name && !f.IsDropped() {
  291. return f.Flag, true
  292. }
  293. }
  294. return 0, false
  295. }
  296. func (table *Table) GetFieldAllocateSize() uint16 { return table.info.FieldAllocateSize }
  297. func (table *Table) GetFirstData() uint32 { return table.info.FirstData }
  298. // GetSchemaExtRef 返回本表首个 schema 续块的位置 (pageID, off); pageID == 0 表示无续块
  299. func (table *Table) GetSchemaExtRef() (uint32, uint16) {
  300. return table.info.ExtPage, table.info.ExtOff
  301. }
  302. func (table *Table) GetFields() []types.FieldInfo {
  303. return table.Field
  304. }
  305. func (table *Table) findFieldByName(name []byte) *types.FieldInfo {
  306. target := util.BytesToString(name)
  307. for i := range table.Field {
  308. if util.BytesToString(table.Field[i].FieldName) == target {
  309. return &table.Field[i]
  310. }
  311. }
  312. return nil
  313. }
  314. // fieldParam 取列级类型参数 (Flag 低 5 位): decimal 是 scale, 字符串家族是行内宽度码,
  315. // 其余类型为 0 (由 types.ValidateColumnParam 保证)。
  316. //
  317. // 以前这里叫 fieldScale 且只服务 decimal; 字符串列也用上同一格之后, "取参数"与
  318. // "算宽度"必须成对 —— 只传 scale 会漏掉宽度码, 编出来的字节宽度与行内不一致。
  319. func (table *Table) fieldParam(masterIdx int) byte {
  320. if masterIdx < 0 || masterIdx >= len(table.Field) {
  321. return 0
  322. }
  323. return table.Field[masterIdx].Param()
  324. }
  325. // AllocPKID 分配并预留下一个自增 pkID。
  326. //
  327. // 供"必须先把号写进对象、再序列化整行"的上层使用 (典型是 ORM:
  328. // 只有先拿到 pkID 填进 _id 列, 存进行里的 _id 才与真实 pkID 一致)。
  329. // 分配即推进游标并预留该号, 紧随其后的 InsertRowWithID 会跳过查重。
  330. // 若最终未插入, 该号只是被跳过 —— 游标只增不减, 不会与后续插入冲突。
  331. func (table *Table) AllocPKID() uint64 {
  332. // 整个"惰性初始化 → 取号 → 游标推进 → 预留登记"在锁内完成:
  333. // 并发写者各拿各的号, 号段互不重叠。
  334. table.pkMu.Lock()
  335. defer table.pkMu.Unlock()
  336. table.ensureNextPK()
  337. pk := table.nextPK
  338. table.nextPK++
  339. table.pendingPK = pk
  340. table.hasPendingPK = true
  341. return pk
  342. }
  343. // ensureNextPK 惰性初始化自增游标: 只增不减, 从行存储里当前最大的存活 pkID 之后继续。
  344. //
  345. // 旧实现 (TotalRows+1) 删除后会回退编号, 与仍存活的 pkID 撞车 (探针 T4)。
  346. // 首次使用时取一次当前最大 pkID —— 每次开表只算一次, 之后 O(1)。
  347. func (table *Table) ensureNextPK() {
  348. if table.nextPKInit {
  349. return
  350. }
  351. table.nextPK = table.maxRowStorePK() + 1
  352. if table.nextPK == 0 {
  353. table.nextPK = 1
  354. }
  355. table.nextPKInit = true
  356. }
  357. // maxRowStorePK 返回行存储索引中当前最大的存活 pkID (无行或没有行存储索引时为 0)。
  358. // DataPage 沿 B+ 树叶链按 pkID 升序排列, 故从最后一页往回找第一个非空页即可;
  359. // 每页内取最大 pkID 以兼容页内乱序的极端情况。
  360. func (table *Table) maxRowStorePK() uint64 {
  361. primMeta, primHandler, ok := table.indexMeta.rowStoreMeta()
  362. if !ok {
  363. return 0
  364. }
  365. ids := primHandler.GetDataPageIDs(table.handle, primMeta.EntryPage)
  366. for i := len(ids) - 1; i >= 0; i-- {
  367. rows := primHandler.ReadDataPageRows(table.handle, ids[i])
  368. var maxPK uint64
  369. for _, r := range rows {
  370. if len(r) < 8 {
  371. continue
  372. }
  373. if pk := binary.BigEndian.Uint64(r[:8]); pk > maxPK {
  374. maxPK = pk
  375. }
  376. }
  377. if maxPK != 0 {
  378. return maxPK
  379. }
  380. }
  381. return 0
  382. }