Table.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. package DB
  2. import (
  3. "git.ycl.pub/yl32940/TsDB2/DB/core"
  4. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  5. "encoding/binary"
  6. "fmt"
  7. "time"
  8. )
  9. // Table 泛型表封装。
  10. //
  11. // T 必须内嵌 DB.BaseModel / DB.PrimaryModel / DB.TimeSeriesModel —— 由 UseTable 在
  12. // 建表之前强制校验 (见 types.go 的 requireBase), 所以拿到一个 *Table[T] 就意味着
  13. // T 一定有对象头部的增删改查。
  14. //
  15. // db 是反向指针, ORM Insert/Update/Delete 自动开 tx (tx.BlobCommit) 时用来从 ORM 层
  16. // 拿 *core.Transaction。UseTable 在创建时填充, 之后只读。
  17. type Table[T any] struct {
  18. db *DB
  19. coreTable *core.Table
  20. model *ModelInfo
  21. name string // 表名
  22. activeColumns map[string]bool // 当前 DB schema 活跃列集合
  23. }
  24. // refreshActiveColumns 从 core table 刷新活跃列集合
  25. func (t *Table[T]) refreshActiveColumns() {
  26. t.activeColumns = make(map[string]bool)
  27. for _, name := range t.coreTable.GetActiveFieldNames() {
  28. t.activeColumns[name] = true
  29. }
  30. }
  31. // SchemaColumns 返回当前 DB 中的所有活跃列名
  32. func (t *Table[T]) SchemaColumns() []string {
  33. names := make([]string, 0, len(t.activeColumns))
  34. for name := range t.activeColumns {
  35. names = append(names, name)
  36. }
  37. return names
  38. }
  39. // Insert 插入一条记录
  40. //
  41. // 顺带把对象绑定到本表, 这样即使对象是外部构造的, 插完之后也能直接用头部的
  42. // Update / Delete / Reload (见 model.go); 同时把引擎分配的行号带回对象, 之后
  43. // obj.Update() / obj.Delete() 才能精确到"这一行"。
  44. //
  45. // Blob 列的落链由 t.db.coreDB (无事务版 BlobCommitter) 承接: 用户不需要手动开事务,
  46. // 写入的内容照样自动落链; 用户若已显式开事务, 链页会经 DB.NewPage 登记进该事务,
  47. // Rollback 整链回收。这里【不】自动 begin-tx + 立即 Commit —— tx.Commit 只要缓冲池
  48. // 里有脏页 (单行插入必然有) 就走完整 WAL + fsync (~37ms/行), 逐行插入会比防抖提交
  49. // 慢三个数量级, 且 SetCommitPreset 的批量策略被整体绕过 (a56b71d 的性能回退根因)。
  50. func (t *Table[T]) Insert(obj *T) error {
  51. t.bindModel(obj)
  52. // 主键显式给了就用它; 没给 (==0) 就向引擎要一个自增号。
  53. // 旧实现无条件用 TotalRows+1 覆盖: 一是调用方填的 _id 被改写 → "按 _id 插入的行
  54. // 按 _id 查不到"; 二是删除会让行数回退 → 号回退并撞上仍存活的 pkID (探针 T4)。
  55. // 现在号一律来自 core 的只增不减游标, 拿到号后再序列化, 保证主键列与 pkID 一致。
  56. // 主键字段可以是内嵌 BaseModel 的 ID, 也可以是用户用 vrtx:"pk" 指定的普通字段。
  57. //
  58. // 时序表是唯一的例外, 见下面 IsTimeSeries 分支。
  59. pkID := uint64(0)
  60. if t.model.IsTimeSeries {
  61. // 时间键不是行号, 两者必须分开: 同一个时间戳下允许有多行 (同一时刻的多个
  62. // 测点 / 多次事件), 这是时序数据的常态。若沿用"时间键即行号", 第二行会被
  63. // 当成重复主键拒掉 —— 数据直接丢, 而这正是引入行号要解决的问题。
  64. // 所以时序表永远向引擎要一个新行号, 时间键保持用户给的原值, 只当索引键用。
  65. pkID = t.coreTable.AllocPKID()
  66. if t.model.PKInStruct {
  67. ts, err := getPKValue(obj, t.model)
  68. if err != nil {
  69. return fmt.Errorf("tsdb: insert: %w", err)
  70. }
  71. if ts == 0 {
  72. // 完全没给时间键: 补一个"写入这一刻"的纳秒级时间戳。
  73. //
  74. // 时序键就是时间戳本身, 默认值必须与显式传入的时间戳同量纲 (纳秒),
  75. // 否则它会成为一个远小于真实时间戳的怪值, 顺序写入下立刻被判为乱序。
  76. // 同一纳秒内连插多行的 TS 会相同, 这是允许的 —— 同刻多行由行号区分。
  77. setPKValue(obj, t.model, uint64(time.Now().UnixNano()))
  78. }
  79. }
  80. } else if t.model.PKInStruct {
  81. var err error
  82. if pkID, err = getPKValue(obj, t.model); err != nil {
  83. return fmt.Errorf("tsdb: insert: %w", err)
  84. }
  85. if pkID == 0 {
  86. pkID = t.coreTable.AllocPKID()
  87. setPKValue(obj, t.model, pkID)
  88. }
  89. }
  90. fvs, err := structToFieldValues(obj, t.model, t.activeColumns, t.db.coreDB)
  91. if err != nil {
  92. return fmt.Errorf("tsdb: insert: %w", err)
  93. }
  94. // pkID == 0 (结构体没有主键字段) 时由 core 自行分配
  95. assigned, err := t.coreTable.InsertRowWithID(pkID, fvs)
  96. if err != nil {
  97. return fmt.Errorf("tsdb: insert: %w", err)
  98. }
  99. setRowIDOf(obj, assigned)
  100. backfillBlobRefs(obj, t.model, fvs)
  101. return nil
  102. }
  103. // BatchInsert 批量插入
  104. //
  105. // 真批量: 走 core 的 InsertRowsWithIDs —— 索引层按"排序灌叶"写入, 数据页/树页
  106. // 落盘按页摊销, saveTableInfo 与防抖提交通知只发一次。灌数吞吐相对逐行 Insert
  107. // 提升一个数量级 (逐行路径每行 4 棵树各下降一次 + 每次页保存整页回写)。
  108. //
  109. // 两种情形退回逐行 Insert, 语义与旧实现完全一致:
  110. // - 时序表: assertTimeOrder 逐行把关写入顺序;
  111. // - 任一对象显式携带主键: 逐行查重路径 (显式主键本身就是小众场景)。
  112. func (t *Table[T]) BatchInsert(objs []*T) error {
  113. if len(objs) == 0 {
  114. return nil
  115. }
  116. if t.model.IsTimeSeries {
  117. for _, obj := range objs {
  118. if err := t.Insert(obj); err != nil {
  119. return err
  120. }
  121. }
  122. return nil
  123. }
  124. // 先扫一遍: 有显式主键就整批退回逐行 (避免"半批半单"两条语义)
  125. if t.model.PKInStruct {
  126. anyExplicit := false
  127. for _, obj := range objs {
  128. t.bindModel(obj)
  129. if pkID, err := getPKValue(obj, t.model); err == nil && pkID != 0 {
  130. anyExplicit = true
  131. break
  132. }
  133. }
  134. if anyExplicit {
  135. for _, obj := range objs {
  136. if err := t.Insert(obj); err != nil {
  137. return err
  138. }
  139. }
  140. return nil
  141. }
  142. }
  143. pkIDs := make([]uint64, len(objs))
  144. fvs := make([][]types.FieldValue, len(objs))
  145. // 事务边界: 用户已持有活跃事务 (显式 BeginTransaction) 就整体复用 —— 既不顶掉
  146. // 用户事务、也不在批量中途替用户全局提交 (那样用户随后的 Rollback 会失去原子性);
  147. // 没有活跃事务才自开自提。core.BeginTransaction 对"已有活跃事务再开"直接 panic,
  148. // 所以必须先查 HasActiveTransaction, 顺序不能反。复用期间 blob 列传 coreDB 版
  149. // committer: 有活跃事务时链页经 NewPage 自动登记进该事务, Rollback 一样整链回收。
  150. ownTx := false
  151. var tx *core.Transaction
  152. var blobc types.BlobCommitter = t.db.coreDB
  153. if !t.db.HasActiveTransaction() {
  154. tx = t.db.BeginTransaction()
  155. ownTx = true
  156. blobc = tx
  157. }
  158. for i, obj := range objs {
  159. t.bindModel(obj)
  160. // 号先拿到、填进对象再序列化: 行里存的 _id 与引擎行号才一致 (与单行同约定)
  161. pkID := t.coreTable.AllocPKID()
  162. if t.model.PKInStruct {
  163. setPKValue(obj, t.model, pkID)
  164. }
  165. pkIDs[i] = pkID
  166. fv, err := structToFieldValues(obj, t.model, t.activeColumns, blobc)
  167. if err != nil {
  168. if ownTx {
  169. _ = tx.Rollback()
  170. }
  171. return fmt.Errorf("tsdb: batch insert: %w", err)
  172. }
  173. fvs[i] = fv
  174. }
  175. // 号全部来自 AllocPKID 预留号段: 走 InsertRowsReserved 跳过逐行查重
  176. // (走 WithIDs 会被当显式主键逐行 rowStoreHasPK —— 正是单行注释里警告过的
  177. // O(N²) 退化, 5 万行实测多烧 1.2GB 分配)
  178. assigned, err := t.coreTable.InsertRowsReserved(pkIDs, fvs)
  179. if err != nil {
  180. if ownTx {
  181. _ = tx.Rollback()
  182. }
  183. return fmt.Errorf("tsdb: batch insert: %w", err)
  184. }
  185. if ownTx {
  186. if cerr := tx.Commit(); cerr != nil {
  187. return fmt.Errorf("tsdb: batch insert: tx commit: %w", cerr)
  188. }
  189. }
  190. for i, obj := range objs {
  191. setRowIDOf(obj, assigned[i])
  192. backfillBlobRefs(obj, t.model, fvs[i])
  193. }
  194. return nil
  195. }
  196. // Update 更新一条记录
  197. //
  198. // 优先按对象携带的引擎行号定位 (从表里读出来或插入过的对象都有), 这样同一时间戳
  199. // 下有多行时改的确实是"自己那一行"; 行号未知时退回按主键列定位 (旧行为)。
  200. //
  201. // Blob 列的落链与旧链回收都由引擎在 UpdateRow 内完成 (新链走 coreDB 的无事务
  202. // BlobCommitter, 旧链走 deferred free), 与 Insert 同一套边界 —— 不自动开 tx。
  203. func (t *Table[T]) Update(obj *T) error {
  204. t.bindModel(obj)
  205. fvs, err := structToFieldValues(obj, t.model, t.activeColumns, t.db.coreDB)
  206. if err != nil {
  207. return fmt.Errorf("tsdb: update: %w", err)
  208. }
  209. if id := rowIDOf(obj); id != 0 {
  210. if uerr := t.coreTable.UpdateRowByPK(id, fvs); uerr != nil {
  211. return fmt.Errorf("tsdb: update: %w", uerr)
  212. }
  213. backfillBlobRefs(obj, t.model, fvs)
  214. return nil
  215. }
  216. pk, err := getPKValue(obj, t.model)
  217. if err != nil {
  218. return fmt.Errorf("tsdb: update: %w", err)
  219. }
  220. if pk == 0 {
  221. return fmt.Errorf("tsdb: update: primary key is 0")
  222. }
  223. if uerr := t.coreTable.UpdateRow(t.pkIndexName(), encodePKKey(pk), fvs); uerr != nil {
  224. return fmt.Errorf("tsdb: update: %w", uerr)
  225. }
  226. backfillBlobRefs(obj, t.model, fvs)
  227. return nil
  228. }
  229. // Delete 按主键删除一条记录
  230. //
  231. // 引擎走 heapReleaseRow + releaseRowBlobRefs 回收堆块与 blob 链, 登记进 deferred
  232. // free 队列 (有活跃事务时随 tx, 无事务时由 Commit/Close 收尾) —— 不自动开 tx。
  233. func (t *Table[T]) Delete(id uint64) error {
  234. return t.coreTable.DeleteRow(t.pkIndexName(), encodePKKey(id))
  235. }
  236. // convertRow 反序列化一行 core 值 → *T, 并回写行号 + 绑定表。
  237. // Find / Scan / Select 三个入口共用。
  238. func (t *Table[T]) convertRow(pkID uint64, row []types.FieldValue) (*T, error) {
  239. obj := new(T)
  240. if err := fieldValuesToStruct(row, t.model, obj, t.coreTable); err != nil {
  241. return nil, err
  242. }
  243. setRowIDOf(obj, pkID)
  244. t.bindModel(obj)
  245. return obj, nil
  246. }
  247. // Find 按主键查找一条记录
  248. func (t *Table[T]) Find(id uint64) (*T, error) {
  249. // SearchByIndexWithPK 与 SearchByIndex 取的是同一行 (同一条索引键 → 同一次定位),
  250. // 只是额外把行的引擎行号带回来 —— 没有它就没法对同刻多行做精确的改 / 删。
  251. pkID, row, err := t.coreTable.SearchByIndexWithPK(t.pkIndexName(), encodePKKey(id))
  252. if err != nil {
  253. return nil, err
  254. }
  255. obj := new(T)
  256. if err := fieldValuesToStruct(row, t.model, obj, t.coreTable); err != nil {
  257. return nil, err
  258. }
  259. setRowIDOf(obj, pkID)
  260. t.bindModel(obj)
  261. return obj, nil
  262. }
  263. // FindAll 返回所有记录
  264. func (t *Table[T]) FindAll() ([]*T, error) {
  265. return t.Query().Find()
  266. }
  267. // Count 返回总行数
  268. func (t *Table[T]) Count() (int64, error) {
  269. return t.coreTable.Query().Count()
  270. }
  271. // Select 流式遍历所有记录
  272. func (t *Table[T]) Select(callback func(*T) bool) error {
  273. var convErr error
  274. err := t.coreTable.Query().ScanWithPKID(func(pkID uint64, row []types.FieldValue) bool {
  275. obj, err := t.convertRow(pkID, row)
  276. if err != nil {
  277. // 反序列化失败: 停止扫描并让 Select 返回错误, 不能静默跳过该行
  278. if convErr == nil {
  279. convErr = err
  280. }
  281. return false
  282. }
  283. return callback(obj)
  284. })
  285. if err != nil {
  286. return err
  287. }
  288. return convErr
  289. }
  290. // Where 开始条件查询
  291. func (t *Table[T]) Where(field string, op string, value interface{}) *Query[T] {
  292. q := &Query[T]{table: t}
  293. q.addWhere(field, op, value)
  294. return q
  295. }
  296. // OrderBy 排序
  297. func (t *Table[T]) OrderBy(field string, desc ...bool) *Query[T] {
  298. q := &Query[T]{table: t}
  299. q.OrderBy(field, desc...)
  300. return q
  301. }
  302. // Limit 限制返回行数
  303. func (t *Table[T]) Limit(n int) *Query[T] {
  304. q := &Query[T]{table: t}
  305. q.Limit(n)
  306. return q
  307. }
  308. // Offset 跳过行数
  309. func (t *Table[T]) Offset(n int) *Query[T] {
  310. q := &Query[T]{table: t}
  311. q.Offset(n)
  312. return q
  313. }
  314. // Query 创建一个新的查询构建器
  315. func (t *Table[T]) Query() *Query[T] {
  316. return &Query[T]{table: t}
  317. }
  318. // BlobReader 返回本表的 blob 读取入口 (core.Table, 实现了 types.BlobReader)。
  319. //
  320. // 用途: 流式载体 types.Blob 的读回 —— row.Body.Open(tbl.BlobReader()) 拿
  321. // io.ReadCloser, 按流消费 (io.Copy / 逐块), 不把内容整块拉进内存; 适合大对象。
  322. // []byte 载体不需要它 (ORM 读回时自动解链)。
  323. func (t *Table[T]) BlobReader() types.BlobReader {
  324. return t.coreTable
  325. }
  326. func (t *Table[T]) pkIndexName() string {
  327. return pkIndexPrefix + t.name
  328. }
  329. // ===== 模型绑定: 头部增删改查的入口 =====
  330. // Bind 把对象绑定到本表 —— 绑定之后才能用对象头部的 Insert / Update / Delete /
  331. // Save / Load / Reload。Insert / Update / Find / FindAll / Select / 条件查询的返回值
  332. // 都会自动绑定, 所以只有"外部手工构造、且第一件事就是 Update/Delete/Reload"的对象
  333. // 需要显式绑一次 (那种情况下用 New 更省事)。
  334. //
  335. // 注意: 绑定信息 (属于哪张表、外层对象是谁) 存在对象内部, 因此
  336. //
  337. // u2 := *u // 拷贝一个已绑定的对象
  338. // u2.Update() // 更新的是 u, 不是 u2
  339. //
  340. // 会更新原对象。需要一份独立的对象请用 Table.New + Load, 或 Table.Find。
  341. func (t *Table[T]) Bind(obj *T) error {
  342. if !t.bindModel(obj) {
  343. // UseTable 已经把没有基类的 T 挡在门外, 走到这里只可能是 T 的绑定器被特殊的
  344. // 嵌入方式挡住了 (例如把基类嵌在一个指针结构体里)。给同样的错误, 别让它静默失效。
  345. return fmt.Errorf("tsdb: %w: %T 需要内嵌 DB.BaseModel / DB.PrimaryModel / "+
  346. "DB.TimeSeriesModel 才能使用头部方法", ErrNotModel, obj)
  347. }
  348. return nil
  349. }
  350. // New 创建一个已经绑定到本表的新对象, 随后可以直接 obj.Load(pk) 读入。
  351. //
  352. // 与"手写 &T{} 再 Bind"的区别: 后者要先拿到 T 的类型; 在泛型代码里 (不知道 T 的具体
  353. // 类型时) 只有 Table 能造出 T, New 就是那个入口。
  354. func (t *Table[T]) New() *T {
  355. obj := new(T)
  356. t.bindModel(obj)
  357. return obj
  358. }
  359. // bindModel 把"这个对象属于哪张表"写回对象内嵌的表基类, 返回是否绑定成功。
  360. //
  361. // 走接口断言而不是反射: 每条被读写的记录都要过一遍, 反射在这里是白付的成本。
  362. // 内嵌的表基类会把 modelBinder 提升到 *T 上, 所以一次类型断言就够了。
  363. func (t *Table[T]) bindModel(obj *T) bool {
  364. b, ok := any(obj).(modelBinder)
  365. if !ok {
  366. return false
  367. }
  368. b.bindTo(t, obj)
  369. return true
  370. }
  371. // ===== session: Table[T] 的类型擦除视图 =====
  372. //
  373. // 模型基类不认识 T, 头部方法只能把请求交回这里; 这一层认识 T, 于是把参数断言回 *T。
  374. // 断言失败只可能是"把 A 表的对象交给了 B 表", 所以返回错误而不是 panic。
  375. // sessionObj 断言出 *T, 并顺手重新绑定 —— 对象可能是刚从一个临时副本拷过来的。
  376. func (t *Table[T]) sessionObj(op string, obj any) (*T, error) {
  377. o, ok := obj.(*T)
  378. if !ok {
  379. return nil, fmt.Errorf("tsdb: %s: 表 %s 收到了 %T, 类型不匹配", op, t.name, obj)
  380. }
  381. t.bindModel(o)
  382. return o, nil
  383. }
  384. func (t *Table[T]) insert(obj any) error {
  385. o, err := t.sessionObj("insert", obj)
  386. if err != nil {
  387. return err
  388. }
  389. return t.Insert(o)
  390. }
  391. func (t *Table[T]) update(obj any) error {
  392. o, err := t.sessionObj("update", obj)
  393. if err != nil {
  394. return err
  395. }
  396. return t.Update(o)
  397. }
  398. func (t *Table[T]) pkOf(obj any) (uint64, error) {
  399. o, err := t.sessionObj("pk", obj)
  400. if err != nil {
  401. return 0, err
  402. }
  403. return getPKValue(o, t.model)
  404. }
  405. // loadInto 把主键 pk 对应的整行读进 obj 指向的对象。
  406. //
  407. // 用 *o = *got 整块赋值而不是逐字段搬: 两边是同一个 T, 字段集合一致, 省掉一次反射。
  408. // 代价是 got 的绑定信息也被拷了过来 (self 还指着 got), 所以紧接着必须重新绑定到 o。
  409. func (t *Table[T]) loadInto(pk uint64, obj any) error {
  410. o, err := t.sessionObj("load", obj)
  411. if err != nil {
  412. return err
  413. }
  414. got, err := t.Find(pk)
  415. if err != nil {
  416. return err
  417. }
  418. *o = *got
  419. t.bindModel(o)
  420. return nil
  421. }
  422. // reload 重新读一遍 obj 所在的那一行。
  423. //
  424. // 行号已知 → 按行号读 (同刻多行也不会读串); 行号未知 → 退回按可见主键读。
  425. func (t *Table[T]) reload(obj any) error {
  426. o, err := t.sessionObj("reload", obj)
  427. if err != nil {
  428. return err
  429. }
  430. if id := rowIDOf(o); id != 0 {
  431. return t.loadByRowID(id, o)
  432. }
  433. pk, err := getPKValue(o, t.model)
  434. if err != nil {
  435. return fmt.Errorf("tsdb: reload: %w", err)
  436. }
  437. return t.loadInto(pk, o)
  438. }
  439. // loadByRowID 把行号 pkID 对应的整行读进 obj 指向的对象。
  440. func (t *Table[T]) loadByRowID(pkID uint64, obj any) error {
  441. o, err := t.sessionObj("load", obj)
  442. if err != nil {
  443. return err
  444. }
  445. got, err := t.findByRowID(pkID)
  446. if err != nil {
  447. return err
  448. }
  449. *o = *got
  450. t.bindModel(o)
  451. return nil
  452. }
  453. // findByRowID 按引擎行号读出一个新对象 (行号是内部句柄, 所以不对外暴露)。
  454. func (t *Table[T]) findByRowID(pkID uint64) (*T, error) {
  455. row, err := t.coreTable.FindByPK(pkID)
  456. if err != nil {
  457. return nil, err
  458. }
  459. obj := new(T)
  460. if err := fieldValuesToStruct(row, t.model, obj, t.coreTable); err != nil {
  461. return nil, err
  462. }
  463. setRowIDOf(obj, pkID)
  464. t.bindModel(obj)
  465. return obj, nil
  466. }
  467. func (t *Table[T]) deleteByPK(pk uint64) error { return t.Delete(pk) }
  468. // deleteByRowID 按引擎行号删除一行 —— 同刻多行时唯一能精确指认"这一行"的方式。
  469. //
  470. // 自动开 tx 与 Delete 同源: heapReleaseRow / releaseRowBlobRefs 都在 deferred.
  471. //
  472. // 事务边界同 BatchInsert: 用户已持有活跃事务则复用、绝不替用户提交 (否则
  473. // 用户的 Rollback 会失去原子性); 无活跃事务才自开自提。必须先查
  474. // HasActiveTransaction 再决定 —— core 对"已有活跃事务再开"直接 panic。
  475. func (t *Table[T]) deleteByRowID(pkID uint64) error {
  476. ownTx := false
  477. var tx *core.Transaction
  478. if !t.db.HasActiveTransaction() {
  479. tx = t.db.BeginTransaction()
  480. ownTx = true
  481. }
  482. if err := t.coreTable.DeleteRowByPK(pkID); err != nil {
  483. if ownTx {
  484. _ = tx.Rollback()
  485. }
  486. return err
  487. }
  488. if ownTx {
  489. if err := tx.Commit(); err != nil {
  490. return fmt.Errorf("tsdb: delete: tx commit: %w", err)
  491. }
  492. }
  493. return nil
  494. }
  495. func (t *Table[T]) tableName() string { return t.name }
  496. func encodePKKey(pk uint64) []byte {
  497. buf := make([]byte, 8)
  498. binary.BigEndian.PutUint64(buf, pk)
  499. return buf
  500. }