main.go 24 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  1. package main
  2. import (
  3. "encoding/binary"
  4. "encoding/json"
  5. "fmt"
  6. "log"
  7. "net/http"
  8. "os"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. "time"
  14. "unsafe"
  15. )
  16. const (
  17. PGSZ = 16384
  18. HDRSZ = 4096
  19. PHDR = 20 // PageHdr actual size (with alignment)
  20. DP_HDR = PHDR + 4 + 8 + 8 // DataPage header: PageHdr(20) + Count(4) + Min(8) + Max(8) = 40
  21. IDX_HDR = 32 // PHDR(20) + pad(1) + IndexType(1) + EntryLen(2) + Count(4) + IsLeaf(1) + pad(3)
  22. META_SZ = 60 // IndexMeta entry size (with Go alignment padding)
  23. TINFO_SZ = 80 // TableInfo struct size (with alignment)
  24. PG_HEAD = 0
  25. PG_TABLE = 1
  26. PG_DATA = 2
  27. PG_HEAP = 3
  28. PG_IMETA = 4
  29. PG_IDATA = 5
  30. )
  31. // ===== Data Structures =====
  32. type TInfo struct {
  33. Name string `json:"name"`
  34. RowN uint64 `json:"rowN"`
  35. FSz uint16 `json:"fsz"`
  36. DP0 uint32 `json:"dp0"`
  37. IP0 uint32 `json:"ip0"`
  38. HP0 uint32 `json:"hp0"`
  39. ActiveVer byte `json:"activeVer"`
  40. MasterFields []MFInfo `json:"masterFields"`
  41. Versions []VInfo `json:"versions"`
  42. Idxs []IInfo `json:"idxs"`
  43. }
  44. type MFInfo struct {
  45. Name string `json:"name"`
  46. Type byte `json:"type"`
  47. TypeName string `json:"typeName"`
  48. Sz int `json:"sz"`
  49. Dropped bool `json:"dropped"`
  50. }
  51. type VInfo struct {
  52. Ver byte `json:"ver"`
  53. Fields []string `json:"fields"`
  54. Indices []byte `json:"indices"`
  55. }
  56. type FInfo struct {
  57. Name string `json:"name"`
  58. Type byte `json:"type"`
  59. Sz int `json:"sz"`
  60. }
  61. type IInfo struct {
  62. Name string `json:"name"`
  63. Type byte `json:"type"`
  64. TS string `json:"ts"`
  65. EP uint32 `json:"ep"`
  66. Flds []int `json:"flds"`
  67. }
  68. type DP struct {
  69. ID uint32 `json:"id"`
  70. Nx uint32 `json:"nx"`
  71. Pv uint32 `json:"pv"`
  72. Cn uint32 `json:"cn"`
  73. Mx uint64 `json:"mx"`
  74. Mn uint64 `json:"mn"`
  75. Us float64 `json:"us"`
  76. Rs []DR `json:"rs"`
  77. }
  78. type DR struct {
  79. I int `json:"i"`
  80. PKID uint64 `json:"pk"`
  81. SchemaV byte `json:"sv"`
  82. Hex string `json:"h"`
  83. Decoded string `json:"decoded"`
  84. }
  85. type IP struct {
  86. ID uint32 `json:"id"`
  87. Nx uint32 `json:"nx"`
  88. Pv uint32 `json:"pv"`
  89. Lf bool `json:"lf"`
  90. Cn uint32 `json:"cn"`
  91. Us float64 `json:"us"`
  92. Ents []IE `json:"ents"`
  93. IdxType byte `json:"it"`
  94. }
  95. type IE struct {
  96. K string `json:"k"`
  97. P uint32 `json:"p"`
  98. DP uint64 `json:"dp"`
  99. }
  100. type HdrInfo struct {
  101. Magic string `json:"magic"`
  102. Version uint32 `json:"ver"`
  103. PageSize uint32 `json:"pgsz"`
  104. NextFree uint32 `json:"nf"`
  105. TablePage uint32 `json:"tp"`
  106. TableCount uint32 `json:"tc"`
  107. CreateTime string `json:"ct"`
  108. ModifyTime string `json:"mt"`
  109. FreeStart uint32 `json:"fs"`
  110. FreeCount uint32 `json:"fc"`
  111. GlobalFlags uint64 `json:"gf"`
  112. Comment string `json:"cm"`
  113. }
  114. type HeapChain struct {
  115. Pages []HeapPageInfo `json:"pages"`
  116. Total int `json:"total"`
  117. }
  118. type HeapPageInfo struct {
  119. ID uint32 `json:"id"`
  120. Next uint32 `json:"nx"`
  121. Prev uint32 `json:"pv"`
  122. Use uint32 `json:"use"`
  123. Us float64 `json:"us"`
  124. }
  125. type FreePages struct {
  126. Pages []uint32 `json:"pages"`
  127. Total int `json:"total"`
  128. }
  129. var (
  130. dbF string
  131. fp string
  132. hdr [HDRSZ]byte
  133. tabs []TInfo
  134. mu sync.RWMutex
  135. )
  136. func main() {
  137. log.SetFlags(log.LstdFlags | log.Lshortfile)
  138. fmt.Println("╔══════════════════════════════════════════════╗")
  139. fmt.Println("║ TsDB2 Analyzer v3.0 (Schema Aware) ║")
  140. fmt.Println("║ http://localhost:8081 ║")
  141. fmt.Println("╚══════════════════════════════════════════════╝")
  142. http.HandleFunc("/", homeH)
  143. http.HandleFunc("/api/list-dir", ldH)
  144. http.HandleFunc("/api/open-db", openH)
  145. http.HandleFunc("/api/stats", stH)
  146. http.HandleFunc("/api/tables", taH)
  147. http.HandleFunc("/api/table/", tdH)
  148. http.HandleFunc("/api/data-page", dpH)
  149. http.HandleFunc("/api/index-page", ipH)
  150. http.HandleFunc("/api/btree", btH)
  151. http.HandleFunc("/api/header", hdrH)
  152. http.HandleFunc("/api/heap-chain", heapH)
  153. http.HandleFunc("/api/free-pages", freeH)
  154. http.HandleFunc("/api/pages", pagesH)
  155. http.HandleFunc("/api/find-pk", findPkH)
  156. http.Handle("/s/", http.StripPrefix("/s/", http.FileServer(http.Dir("."))))
  157. log.Fatal(http.ListenAndServe(":8081", nil))
  158. }
  159. // ===== Low-level page reading =====
  160. func rp(pid uint32) ([]byte, error) {
  161. off := int64(HDRSZ) + int64(pid-1)*int64(PGSZ)
  162. b := make([]byte, PGSZ)
  163. f, e := os.Open(fp)
  164. if e != nil {
  165. return nil, e
  166. }
  167. defer f.Close()
  168. _, e = f.ReadAt(b, off)
  169. return b, e
  170. }
  171. func rHdr() {
  172. f, e := os.Open(fp)
  173. if e != nil {
  174. return
  175. }
  176. defer f.Close()
  177. f.ReadAt(hdr[:], 0)
  178. }
  179. // ===== Parse all tables from TablePage chain =====
  180. func pTabs() []TInfo {
  181. var R []TInfo
  182. b := make([]byte, PGSZ)
  183. f, e := os.Open(fp)
  184. if e != nil {
  185. return R
  186. }
  187. defer f.Close()
  188. tp := binary.LittleEndian.Uint32(hdr[16:20])
  189. if tp == 0 {
  190. return R
  191. }
  192. for pid := tp; pid != 0; {
  193. f.ReadAt(b, int64(HDRSZ)+int64(pid-1)*int64(PGSZ))
  194. us := binary.LittleEndian.Uint32(b[16:20])
  195. if us > PGSZ {
  196. us = PGSZ
  197. }
  198. off := uint32(PHDR)
  199. for off+TINFO_SZ <= us && off+TINFO_SZ <= PGSZ {
  200. tb := b[off : off+TINFO_SZ]
  201. fl := tb[24]
  202. if fl&0x80 != 0 {
  203. off += TINFO_SZ + uint32(binary.LittleEndian.Uint16(tb[58:60]))
  204. continue
  205. }
  206. nm := strings.TrimRight(string(tb[:24]), "\x00")
  207. rn := binary.LittleEndian.Uint64(tb[32:40])
  208. // Parse new schema format
  209. fo := off + TINFO_SZ
  210. activeVer, masterFields, versions := parseSchemaFields(b, fo)
  211. // Parse indexes
  212. var is []IInfo
  213. iip := binary.LittleEndian.Uint32(tb[64:68])
  214. for ip := iip; ip != 0; {
  215. ib, e := rp(ip)
  216. if e != nil {
  217. break
  218. }
  219. if ib[0] != PG_IMETA {
  220. break
  221. }
  222. iu := binary.LittleEndian.Uint32(ib[16:20])
  223. if iu > PGSZ {
  224. iu = PGSZ
  225. }
  226. io := uint32(PHDR)
  227. for io+META_SZ <= iu && io+META_SZ <= PGSZ {
  228. im := ib[io : io+META_SZ]
  229. // IndexMeta struct: Name[24] + Type(1) + FieldCnt(1) + FieldIDs[16] + pad(2) + EntryPage(4) + Prev(4) + Next(4) + Flag(1) + pad(3) = 60
  230. if im[56]&0x80 == 0 { // Flag at offset 56
  231. in := strings.TrimRight(string(im[:24]), "\x00")
  232. it := im[24] // IndexType at offset 24
  233. ic := int(im[25]) // FieldCnt at offset 25
  234. ep := binary.LittleEndian.Uint32(im[44:48]) // EntryPage at offset 44 (after 2B padding)
  235. // 验证 EntryPage 指向有效索引页 (防止已删除表的孤立条目)
  236. if ep != 0 {
  237. pb, pe := rp(ep)
  238. if pe != nil || pb[0] != PG_IDATA {
  239. ep = 0 // 无效页, 跳过
  240. }
  241. }
  242. if ep != 0 {
  243. var fds []int
  244. for j := 0; j < ic; j++ {
  245. fds = append(fds, int(im[26+j])) // FieldIDs at offset 26
  246. }
  247. is = append(is, IInfo{in, it, tNm(it), ep, fds})
  248. }
  249. }
  250. io += META_SZ
  251. }
  252. ip = binary.LittleEndian.Uint32(ib[8:12])
  253. }
  254. R = append(R, TInfo{
  255. Name: nm, RowN: rn,
  256. FSz: binary.LittleEndian.Uint16(tb[56:58]),
  257. DP0: binary.LittleEndian.Uint32(tb[48:52]),
  258. IP0: binary.LittleEndian.Uint32(tb[64:68]),
  259. HP0: binary.LittleEndian.Uint32(tb[40:44]),
  260. ActiveVer: activeVer, MasterFields: masterFields, Versions: versions, Idxs: is,
  261. })
  262. off += TINFO_SZ + uint32(binary.LittleEndian.Uint16(tb[58:60]))
  263. }
  264. nx := binary.LittleEndian.Uint32(b[8:12])
  265. if nx == 0 || nx == pid {
  266. break
  267. }
  268. pid = nx
  269. }
  270. return R
  271. }
  272. // parseSchemaFields 解析新版 schema 字段定义链
  273. // 格式: [activeVer(1B)] [masterCount(1B)] [type+len+name]×N [verCount(1B)] [verDefs...]
  274. func parseSchemaFields(b []byte, off uint32) (byte, []MFInfo, []VInfo) {
  275. if int(off)+2 > len(b) {
  276. return 0, nil, nil
  277. }
  278. pos := int(off)
  279. activeVer := b[pos]
  280. pos++
  281. masterCount := int(b[pos])
  282. pos++
  283. var masterFields []MFInfo
  284. for i := 0; i < masterCount && pos+2 <= len(b); i++ {
  285. typ := b[pos]
  286. nameLen := int(b[pos+1])
  287. pos += 2
  288. if pos+nameLen > len(b) {
  289. break
  290. }
  291. name := strings.TrimRight(string(b[pos:pos+nameLen]), "\x00")
  292. pos += nameLen
  293. dropped := typ&0x80 != 0
  294. realType := typ & 0x7F
  295. masterFields = append(masterFields, MFInfo{
  296. Name: name, Type: realType, TypeName: fName(realType),
  297. Sz: fSz(realType), Dropped: dropped,
  298. })
  299. }
  300. var versions []VInfo
  301. if pos < len(b) {
  302. verCount := int(b[pos])
  303. pos++
  304. for i := 0; i < verCount && pos < len(b); i++ {
  305. verNo := b[pos]
  306. pos++
  307. if pos >= len(b) {
  308. break
  309. }
  310. fieldCount := int(b[pos])
  311. pos++
  312. indices := make([]byte, fieldCount)
  313. if pos+fieldCount <= len(b) {
  314. copy(indices, b[pos:pos+fieldCount])
  315. pos += fieldCount
  316. }
  317. // Build field name list for this version
  318. var fieldNames []string
  319. for _, idx := range indices {
  320. if int(idx) < len(masterFields) {
  321. mf := masterFields[idx]
  322. n := mf.Name
  323. if mf.Dropped {
  324. n += " (dropped)"
  325. }
  326. fieldNames = append(fieldNames, n)
  327. } else {
  328. fieldNames = append(fieldNames, "?")
  329. }
  330. }
  331. versions = append(versions, VInfo{Ver: verNo, Fields: fieldNames, Indices: indices})
  332. }
  333. }
  334. return activeVer, masterFields, versions
  335. }
  336. // ===== Field type helpers =====
  337. func fSz(t byte) int {
  338. switch t {
  339. case 1:
  340. return 1
  341. case 2:
  342. return 2
  343. case 3:
  344. return 4
  345. case 4:
  346. return 8
  347. case 5:
  348. return 16
  349. case 6:
  350. return 32
  351. case 7:
  352. return 64
  353. case 8:
  354. return 128
  355. case 9:
  356. return 256
  357. default:
  358. return 0
  359. }
  360. }
  361. func tNm(t byte) string {
  362. switch t {
  363. case 1:
  364. return "Time"
  365. case 2:
  366. return "Primary"
  367. case 3:
  368. return "String"
  369. case 4:
  370. return "Compose"
  371. case 5:
  372. return "BTree"
  373. default:
  374. return "?"
  375. }
  376. }
  377. func fName(t byte) string {
  378. switch t {
  379. case 1:
  380. return "int8"
  381. case 2:
  382. return "int16"
  383. case 3:
  384. return "int32"
  385. case 4:
  386. return "int64"
  387. case 5:
  388. return "16byte"
  389. case 6:
  390. return "32byte"
  391. case 7:
  392. return "64byte"
  393. case 8:
  394. return "128byte"
  395. case 9:
  396. return "256byte"
  397. default:
  398. return "?"
  399. }
  400. }
  401. // ===== Read DataPage with schema version awareness =====
  402. // rDP reads a DataPage (sequential row layout, no directory)
  403. func rDP(pid uint32) *DP {
  404. b, e := rp(pid)
  405. if e != nil {
  406. return nil
  407. }
  408. if b[0] != PG_DATA {
  409. return nil
  410. }
  411. cn := binary.LittleEndian.Uint32(b[PHDR : PHDR+4])
  412. mn := binary.BigEndian.Uint64(b[PHDR+4 : PHDR+12])
  413. mx := binary.BigEndian.Uint64(b[PHDR+12 : PHDR+20])
  414. us := float64(binary.LittleEndian.Uint32(b[16:20])) / float64(PGSZ) * 100
  415. var rs []DR
  416. off := DP_HDR
  417. for i := uint32(0); i < cn && off+4 <= len(b); i++ {
  418. rl := int(binary.LittleEndian.Uint32(b[off : off+4]))
  419. off += 4
  420. if off+rl > len(b) || rl <= 0 {
  421. break
  422. }
  423. storeRow := b[off : off+rl]
  424. off += rl
  425. // storeRow = [pkID(8B BE)] + rowData
  426. var pkID uint64
  427. var rowData []byte
  428. if len(storeRow) >= 8 {
  429. pkID = binary.BigEndian.Uint64(storeRow[:8])
  430. rowData = storeRow[8:]
  431. } else {
  432. rowData = storeRow
  433. }
  434. schemaV := byte(0)
  435. if len(rowData) > 0 {
  436. schemaV = rowData[0]
  437. }
  438. hx := fmt.Sprintf("%x", storeRow[:min(len(storeRow), 64)])
  439. if len(storeRow) > 64 {
  440. hx += "..."
  441. }
  442. decoded := decodeRowData(rowData, schemaV)
  443. rs = append(rs, DR{int(i), pkID, schemaV, hx, decoded})
  444. }
  445. return &DP{pid, binary.LittleEndian.Uint32(b[8:12]), binary.LittleEndian.Uint32(b[12:16]),
  446. cn, mx, mn, us, rs}
  447. }
  448. // decodeRowData 尝试解码行数据 (显示 schemaVer + 各字段值)
  449. func decodeRowData(rowData []byte, schemaV byte) string {
  450. if len(rowData) < 1 {
  451. return ""
  452. }
  453. // 查找对应表的版本信息来解码
  454. // 简单解码: 显示 schemaVer + hex
  455. parts := []string{fmt.Sprintf("v%d", schemaV)}
  456. data := rowData[1:] // skip schemaVer
  457. if len(data) == 0 {
  458. return strings.Join(parts, " ")
  459. }
  460. // 尝试按字段类型解码 (需要知道该版本的字段列表)
  461. // 通用方式: 显示前几个字节的值
  462. for _, t := range tabs {
  463. sv := findVersion(t, schemaV)
  464. if sv == nil {
  465. continue
  466. }
  467. off := 0
  468. for _, idx := range sv.Indices {
  469. if int(idx) >= len(t.MasterFields) {
  470. break
  471. }
  472. mf := t.MasterFields[idx]
  473. if mf.Dropped {
  474. continue
  475. }
  476. sz := mf.Sz
  477. if off+sz > len(data) {
  478. break
  479. }
  480. val := decodeFieldValue(data[off:off+sz], mf.Type)
  481. parts = append(parts, mf.Name+"="+val)
  482. off += sz
  483. }
  484. return strings.Join(parts, ", ")
  485. }
  486. return strings.Join(parts, " ")
  487. }
  488. func findVersion(t TInfo, ver byte) *VInfo {
  489. for i := range t.Versions {
  490. if t.Versions[i].Ver == ver {
  491. return &t.Versions[i]
  492. }
  493. }
  494. return nil
  495. }
  496. func decodeFieldValue(buf []byte, typ byte) string {
  497. switch typ {
  498. case 1:
  499. return fmt.Sprintf("%d", int8(buf[0]))
  500. case 2:
  501. return fmt.Sprintf("%d", int16(binary.BigEndian.Uint16(buf)))
  502. case 3:
  503. return fmt.Sprintf("%d", int32(binary.BigEndian.Uint32(buf)))
  504. case 4:
  505. return fmt.Sprintf("%d", int64(binary.BigEndian.Uint64(buf)))
  506. default:
  507. s := strings.TrimRight(string(buf), "\x00")
  508. if len(s) == 0 {
  509. return "(empty)"
  510. }
  511. return s
  512. }
  513. }
  514. // ===== Index page reading =====
  515. func rIP(pid uint32, it byte) *IP {
  516. b, e := rp(pid)
  517. if e != nil {
  518. return nil
  519. }
  520. if b[0] != PG_IDATA {
  521. return nil
  522. }
  523. lf := b[28] == 1
  524. cn := binary.LittleEndian.Uint32(b[24:28])
  525. el := int(binary.LittleEndian.Uint16(b[22:24]))
  526. if el <= 4 {
  527. el = 12
  528. }
  529. ents := pEE(b, int(cn), el, it, lf)
  530. us := float64(binary.LittleEndian.Uint32(b[16:20])) / float64(PGSZ) * 100
  531. return &IP{pid, binary.LittleEndian.Uint32(b[8:12]), binary.LittleEndian.Uint32(b[12:16]),
  532. lf, cn, us, ents, it}
  533. }
  534. func pEE(b []byte, cn, el int, it byte, lf bool) []IE {
  535. var ents []IE
  536. for i := 0; i < cn; i++ {
  537. o := IDX_HDR + i*el
  538. if o+el > len(b) {
  539. break
  540. }
  541. entry := b[o : o+el]
  542. var ie IE
  543. switch it {
  544. case 2:
  545. ie.K = fmt.Sprintf("%d", binary.BigEndian.Uint64(entry[:8]))
  546. ie.P = binary.BigEndian.Uint32(entry[8:12])
  547. case 1:
  548. ie.K = fmt.Sprintf("%d ~ %d", binary.BigEndian.Uint64(entry[:8]), binary.BigEndian.Uint64(entry[8:16]))
  549. ie.P = binary.BigEndian.Uint32(entry[16:20])
  550. case 3:
  551. ie.K = strings.TrimRight(string(entry[:16]), "\x00")
  552. ie.DP = binary.BigEndian.Uint64(entry[16:24])
  553. if lf {
  554. ie.P = binary.BigEndian.Uint32(entry[20:24])
  555. }
  556. case 4:
  557. nk := (el - 8) / 8
  558. parts := make([]string, nk)
  559. for j := 0; j < nk; j++ {
  560. parts[j] = fmt.Sprintf("%d", binary.BigEndian.Uint64(entry[j*8:]))
  561. }
  562. ie.K = strings.Join(parts, ", ")
  563. ie.DP = binary.BigEndian.Uint64(entry[nk*8:])
  564. if lf {
  565. ie.P = binary.BigEndian.Uint32(entry[el-4:])
  566. }
  567. case 5:
  568. if lf {
  569. kl := el - 8
  570. if kl == 8 {
  571. ie.K = fmt.Sprintf("%d", binary.BigEndian.Uint64(entry[:8]))
  572. } else {
  573. ie.K = fmt.Sprintf("%x", entry[:kl])
  574. }
  575. ie.DP = binary.BigEndian.Uint64(entry[kl:])
  576. ie.P = binary.BigEndian.Uint32(entry[el-4:])
  577. } else {
  578. kl := el - 4
  579. if kl == 8 {
  580. ie.K = fmt.Sprintf("%d", binary.BigEndian.Uint64(entry[:8]))
  581. } else {
  582. ie.K = fmt.Sprintf("%x", entry[:kl])
  583. }
  584. ie.P = binary.BigEndian.Uint32(entry[el-4:])
  585. }
  586. default:
  587. ie.K = fmt.Sprintf("%x", entry[:el-4])
  588. ie.P = binary.BigEndian.Uint32(entry[el-4:])
  589. }
  590. ents = append(ents, ie)
  591. }
  592. return ents
  593. }
  594. func rBT(pid uint32, it byte) *IP { return rIP(pid, it) }
  595. // ===== Header + Heap + Free =====
  596. func rHdrInfo() *HdrInfo {
  597. f, e := os.Open(fp)
  598. if e != nil {
  599. return nil
  600. }
  601. defer f.Close()
  602. b := make([]byte, 4096)
  603. f.ReadAt(b, 0)
  604. magic := string(b[:4])
  605. ver := binary.LittleEndian.Uint32(b[4:8])
  606. pgsz := binary.LittleEndian.Uint32(b[8:12])
  607. nf := binary.LittleEndian.Uint32(b[12:16])
  608. tp := binary.LittleEndian.Uint32(b[16:20])
  609. tc := binary.LittleEndian.Uint32(b[20:24])
  610. ct := int64(binary.LittleEndian.Uint64(b[24:32]))
  611. mt := int64(binary.LittleEndian.Uint64(b[32:40]))
  612. fs := binary.LittleEndian.Uint32(b[40:44])
  613. fc := binary.LittleEndian.Uint32(b[44:48])
  614. gf := binary.LittleEndian.Uint64(b[48:56])
  615. cm := strings.TrimRight(string(b[312:824]), "\x00")
  616. return &HdrInfo{magic, ver, pgsz, nf, tp, tc,
  617. time.Unix(0, ct).Format("2006-01-02 15:04:05"),
  618. time.Unix(0, mt).Format("2006-01-02 15:04:05"),
  619. fs, fc, gf, cm}
  620. }
  621. func rHeapChain(hp0 uint32) *HeapChain {
  622. var pages []HeapPageInfo
  623. for pid := hp0; pid != 0; {
  624. b, e := rp(pid)
  625. if e != nil || b[0] != PG_HEAP {
  626. break
  627. }
  628. nx := binary.LittleEndian.Uint32(b[8:12])
  629. pv := binary.LittleEndian.Uint32(b[12:16])
  630. use := binary.LittleEndian.Uint32(b[16:20])
  631. us := float64(use) / float64(PGSZ) * 100
  632. pages = append(pages, HeapPageInfo{pid, nx, pv, use, us})
  633. if nx == pid || nx == 0 {
  634. break
  635. }
  636. pid = nx
  637. }
  638. return &HeapChain{pages, len(pages)}
  639. }
  640. func rFreePages() *FreePages {
  641. f, e := os.Open(fp)
  642. if e != nil {
  643. return &FreePages{nil, 0}
  644. }
  645. defer f.Close()
  646. freeStart := binary.LittleEndian.Uint32(hdr[40:44])
  647. var pages []uint32
  648. for pid := freeStart; pid != 0; {
  649. pages = append(pages, pid)
  650. b := make([]byte, PGSZ)
  651. off := int64(HDRSZ) + int64(pid-1)*int64(PGSZ)
  652. if _, e := f.ReadAt(b, off); e != nil {
  653. break
  654. }
  655. nx := binary.BigEndian.Uint32(b[:4])
  656. if nx == pid || nx == 0 {
  657. break
  658. }
  659. pid = nx
  660. }
  661. return &FreePages{pages, len(pages)}
  662. }
  663. // ===== Page visualization =====
  664. type PageItem struct {
  665. ID uint32 `json:"id"`
  666. Type int `json:"t"`
  667. TS string `json:"ts"`
  668. Us float64 `json:"us"`
  669. }
  670. type PagesResult struct {
  671. Pages []PageItem `json:"pages"`
  672. Total int `json:"total"`
  673. Counts map[string]int `json:"counts"`
  674. }
  675. func rPages() *PagesResult {
  676. fi, e := os.Stat(fp)
  677. if e != nil {
  678. return &PagesResult{}
  679. }
  680. totalPg := int(fi.Size() / PGSZ)
  681. if totalPg <= 1 {
  682. return &PagesResult{}
  683. }
  684. // page 0 is not used (header is separate), pages start from 1
  685. numPages := totalPg - 1 // subtract header page
  686. if numPages <= 0 {
  687. return &PagesResult{}
  688. }
  689. f, e := os.Open(fp)
  690. if e != nil {
  691. return &PagesResult{}
  692. }
  693. defer f.Close()
  694. pages := make([]PageItem, 0, numPages)
  695. counts := map[string]int{"hdr": 0, "table": 0, "data": 0, "heap": 0, "imeta": 0, "idata": 0, "free": 0, "other": 0}
  696. typeNames := map[int]string{0: "free", 1: "table", 2: "data", 3: "heap", 4: "imeta", 5: "idata"}
  697. // Read free page list for marking
  698. freeSet := make(map[uint32]bool)
  699. freeStart := binary.LittleEndian.Uint32(hdr[40:44])
  700. for pid := freeStart; pid != 0; {
  701. freeSet[pid] = true
  702. b := make([]byte, PGSZ)
  703. off := int64(HDRSZ) + int64(pid-1)*int64(PGSZ)
  704. if _, e := f.ReadAt(b, off); e != nil {
  705. break
  706. }
  707. nx := binary.BigEndian.Uint32(b[:4])
  708. if nx == pid || nx == 0 {
  709. break
  710. }
  711. pid = nx
  712. }
  713. for i := 1; i <= numPages; i++ {
  714. pid := uint32(i)
  715. b := make([]byte, PGSZ)
  716. off := int64(HDRSZ) + int64(pid-1)*int64(PGSZ)
  717. if _, e := f.ReadAt(b, off); e != nil {
  718. break
  719. }
  720. pt := int(b[0])
  721. us := float64(binary.LittleEndian.Uint32(b[16:20])) / float64(PGSZ) * 100
  722. if us > 100 {
  723. us = 100
  724. }
  725. if freeSet[pid] {
  726. pt = 0 // mark as free
  727. }
  728. tn := typeNames[pt]
  729. if tn == "" {
  730. tn = "other"
  731. }
  732. counts[tn]++
  733. pages = append(pages, PageItem{pid, pt, tn, us})
  734. }
  735. return &PagesResult{pages, len(pages), counts}
  736. }
  737. func pagesH(w http.ResponseWriter, r *http.Request) {
  738. if fp == "" {
  739. jw(w, map[string]any{"e": "no db"})
  740. return
  741. }
  742. jw(w, rPages())
  743. }
  744. // findPkH 通过主键 B+ 树查找 PKID 对应的 DataPage
  745. func findPkH(w http.ResponseWriter, r *http.Request) {
  746. if fp == "" {
  747. jw(w, map[string]any{"e": "no db"})
  748. return
  749. }
  750. pkStr := r.URL.Query().Get("pk")
  751. rootStr := r.URL.Query().Get("root")
  752. var pk, root uint32
  753. fmt.Sscanf(pkStr, "%d", &pk)
  754. fmt.Sscanf(rootStr, "%d", &root)
  755. if root == 0 {
  756. jw(w, map[string]any{"e": "no root"})
  757. return
  758. }
  759. dp := findDPByPK(root, uint64(pk))
  760. if dp == 0 {
  761. jw(w, map[string]any{"e": "not found"})
  762. return
  763. }
  764. jw(w, map[string]any{"dp": dp, "pk": pk})
  765. }
  766. // findDPByPK 在主键 B+ 树中查找 PKID 对应的 DataPage ID
  767. func findDPByPK(rootPID uint32, targetPK uint64) uint32 {
  768. cur := rootPID
  769. for {
  770. b, e := rp(cur)
  771. if e != nil || b[0] != PG_IDATA {
  772. return 0
  773. }
  774. lf := b[28] == 1
  775. cn := int(binary.LittleEndian.Uint32(b[24:28]))
  776. el := int(binary.LittleEndian.Uint16(b[22:24]))
  777. if el <= 4 {
  778. el = 12
  779. }
  780. if lf {
  781. // 叶子节点: 查找匹配的 PK
  782. for i := 0; i < cn; i++ {
  783. o := IDX_HDR + i*el
  784. if o+el > len(b) {
  785. break
  786. }
  787. key := binary.BigEndian.Uint64(b[o : o+8])
  788. if key == targetPK {
  789. return binary.BigEndian.Uint32(b[o+8 : o+12])
  790. }
  791. }
  792. return 0
  793. }
  794. // 内部节点: 二分查找最后一个 key <= targetPK
  795. lo, hi := 0, cn-1
  796. pos := 0
  797. for lo <= hi {
  798. mid := (lo + hi) / 2
  799. o := IDX_HDR + mid*el
  800. if o+8 > len(b) {
  801. break
  802. }
  803. key := binary.BigEndian.Uint64(b[o : o+8])
  804. if key <= targetPK {
  805. pos = mid
  806. lo = mid + 1
  807. } else {
  808. hi = mid - 1
  809. }
  810. }
  811. o := IDX_HDR + pos*el
  812. childPID := binary.BigEndian.Uint32(b[o+el-4 : o+el])
  813. if childPID == 0 {
  814. return 0
  815. }
  816. cur = childPID
  817. }
  818. }
  819. // ===== Utility =====
  820. func min(a, b int) int {
  821. if a < b {
  822. return a
  823. }
  824. return b
  825. }
  826. func fB(n int64) string {
  827. if n < 1024 {
  828. return fmt.Sprintf("%d B", n)
  829. }
  830. if n < 1048576 {
  831. return fmt.Sprintf("%.1f KB", float64(n)/1024)
  832. }
  833. if n < 1073741824 {
  834. return fmt.Sprintf("%.2f MB", float64(n)/1048576)
  835. }
  836. return fmt.Sprintf("%.2f GB", float64(n)/1073741824)
  837. }
  838. func fE(p string) bool { _, e := os.Stat(p); return e == nil }
  839. func jw(w http.ResponseWriter, v any) {
  840. w.Header().Set("Content-Type", "application/json")
  841. json.NewEncoder(w).Encode(v)
  842. }
  843. // ===== HTTP Handlers =====
  844. func homeH(w http.ResponseWriter, r *http.Request) {
  845. w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate")
  846. w.Write([]byte(pg))
  847. }
  848. func ldH(w http.ResponseWriter, r *http.Request) {
  849. p := r.URL.Query().Get("path")
  850. if p == "" {
  851. p, _ = os.Getwd()
  852. }
  853. if !filepath.IsAbs(p) {
  854. c, _ := os.Getwd()
  855. p = filepath.Join(c, p)
  856. }
  857. es, e := os.ReadDir(p)
  858. if e != nil {
  859. jw(w, map[string]any{"err": e.Error()})
  860. return
  861. }
  862. type F struct {
  863. N string `json:"n"`
  864. D bool `json:"d"`
  865. S int64 `json:"s"`
  866. }
  867. var ds, fs []F
  868. for _, x := range es {
  869. i, _ := x.Info()
  870. s := int64(0)
  871. if i != nil {
  872. s = i.Size()
  873. }
  874. if x.IsDir() {
  875. ds = append(ds, F{x.Name(), true, s})
  876. } else {
  877. fs = append(fs, F{x.Name(), false, s})
  878. }
  879. }
  880. jw(w, map[string]any{"p": p, "pp": filepath.Dir(p), "d": ds, "f": fs})
  881. }
  882. func openH(w http.ResponseWriter, r *http.Request) {
  883. if r.Method != "POST" {
  884. http.Error(w, "no", 405)
  885. return
  886. }
  887. var q struct {
  888. P string `json:"p"`
  889. }
  890. json.NewDecoder(r.Body).Decode(&q)
  891. if !fE(q.P) {
  892. c, _ := os.Getwd()
  893. q2 := filepath.Join(c, q.P)
  894. if fE(q2) {
  895. q.P = q2
  896. } else {
  897. jw(w, map[string]any{"ok": false, "e": "not found"})
  898. return
  899. }
  900. }
  901. mu.Lock()
  902. fp = q.P
  903. rHdr()
  904. tabs = pTabs()
  905. mu.Unlock()
  906. fi, _ := os.Stat(fp)
  907. dbF = fp
  908. jw(w, map[string]any{"ok": true, "s": st(fi)})
  909. }
  910. func st(fi os.FileInfo) map[string]any {
  911. s := int64(0)
  912. if fi != nil {
  913. s = fi.Size()
  914. }
  915. return map[string]any{"f": dbF, "s": s, "fs": fB(s), "pg": int(s / PGSZ), "tc": len(tabs)}
  916. }
  917. func stH(w http.ResponseWriter, r *http.Request) {
  918. if dbF == "" {
  919. jw(w, map[string]any{"e": "no db"})
  920. return
  921. }
  922. fi, _ := os.Stat(fp)
  923. jw(w, st(fi))
  924. }
  925. func taH(w http.ResponseWriter, r *http.Request) { jw(w, tabs) }
  926. func tdH(w http.ResponseWriter, r *http.Request) {
  927. n := r.URL.Path[len("/api/table/"):]
  928. for _, t := range tabs {
  929. if t.Name == n {
  930. jw(w, t)
  931. return
  932. }
  933. }
  934. jw(w, map[string]any{"e": "not found"})
  935. }
  936. func dpH(w http.ResponseWriter, r *http.Request) {
  937. var pid uint32
  938. fmt.Sscanf(r.URL.Query().Get("id"), "%d", &pid)
  939. d := rDP(pid)
  940. if d == nil {
  941. jw(w, map[string]any{"e": "err"})
  942. return
  943. }
  944. jw(w, d)
  945. }
  946. func ipH(w http.ResponseWriter, r *http.Request) {
  947. var pid uint32
  948. fmt.Sscanf(r.URL.Query().Get("id"), "%d", &pid)
  949. it, _ := strconv.Atoi(r.URL.Query().Get("t"))
  950. d := rIP(pid, byte(it))
  951. if d == nil {
  952. jw(w, map[string]any{"e": "err"})
  953. return
  954. }
  955. jw(w, d)
  956. }
  957. func btH(w http.ResponseWriter, r *http.Request) {
  958. var pid uint32
  959. fmt.Sscanf(r.URL.Query().Get("id"), "%d", &pid)
  960. it, _ := strconv.Atoi(r.URL.Query().Get("t"))
  961. d := rBT(pid, byte(it))
  962. if d == nil {
  963. jw(w, map[string]any{"e": "err"})
  964. return
  965. }
  966. jw(w, d)
  967. }
  968. func hdrH(w http.ResponseWriter, r *http.Request) {
  969. d := rHdrInfo()
  970. if d == nil {
  971. jw(w, map[string]any{"e": "err"})
  972. return
  973. }
  974. jw(w, d)
  975. }
  976. func heapH(w http.ResponseWriter, r *http.Request) {
  977. var hp0 uint32
  978. fmt.Sscanf(r.URL.Query().Get("id"), "%d", &hp0)
  979. jw(w, rHeapChain(hp0))
  980. }
  981. func freeH(w http.ResponseWriter, r *http.Request) { jw(w, rFreePages()) }
  982. var _ = unsafe.Sizeof(0)