Explorar o código

feat(format): Comment 扩展为 3764 字节占满整页, 建库默认写入版本介绍

- HeaderPage 保留区 (3252B) 并入 Comment: Comment[PageSize4K-332] =
  3764 字节, 字段区 332 字节 + 介绍区 3764 字节 = 整页 4096, 编译期
  断言钉死容量与偏移
- 新增 VersionComment (当前版本 v11 的功能介绍与修复点); newHeader
  收到空介绍时默认写入; Open 建库传空串走默认
- SetComment 上限随容量变为 3764 字节; analyzer 读取范围 332:4096
- 测试更新: 默认介绍=VersionComment; 边界 3764 通过 / 3765 报错
yl32940 hai 3 semanas
pai
achega
f8c7817eb4
Modificáronse 6 ficheiros con 61 adicións e 28 borrados
  1. 15 0
      DB/core/const.go
  2. 4 2
      DB/core/db.go
  3. 17 9
      DB/core/header_comment_test.go
  4. 16 10
      DB/core/header_page.go
  5. 8 6
      README.md
  6. 1 1
      analyzer/main.go

+ 15 - 0
DB/core/const.go

@@ -46,6 +46,21 @@ const (
 	//     旧库不兼容, 需重建。
 	Version = 11
 
+	// VersionComment 当前格式版本的【功能介绍与修复点】。建库时若调用方没有
+	// 提供介绍 (newHeader 传空串), 头页 Comment 区默认写入本文案 —— 拿到一个
+	// .db 文件的人从头页就能读到这个库是什么版本、有什么能力。
+	//
+	// 约束: 必须 ≤ HeaderPage.Comment 的容量 (PageSize4K-844 = 3252 字节),
+	// 超长会在编译期由 header_page.go 的断言挡住? 不会 — 是 newHeader 运行期报错。
+	// 改版本时同步更新本文案。
+	VersionComment = `TsDB2 (VRTX) 格式版本 v11 — 功能与修复:
+· HeaderPage 重排: FreePageEnd 紧邻 FreePageStart, 字段 8 字节对齐零填充; Comment 扩为 3252B 介绍区
+· 查询执行器: IN 走索引 / 二级索引批量回表 / ORDER BY+LIMIT 流式 top-k / Select 投影下推
+· BufferPool 真 LRU + 读缓存; WAL 提交链优化; 无唯一索引表 Update 跳过预检序列化
+· 复合索引处理器 = SBT B+ 树, 分量不限 8 字节
+· 正确性: 事务/崩溃恢复的页回收时序 (删空页延迟还链), VACUUM 重建保真 (索引名/类型/NULL), ORM 查询错误传播
+· 结构: RowStoreHandler 能力接口, table_*.go / query_*.go / btree 生命周期拆分`
+
 	// PageSize 数据页大小 (16KB) — 索引页、数据页、WAL 页统一使用
 	PageSize uint32 = 4096 * 4
 

+ 4 - 2
DB/core/db.go

@@ -121,7 +121,9 @@ func Open(path string) (*DB, error) {
 
 	var header *HeaderPage
 	if stat.Size() == 0 {
-		header, err = newHeader(fmt.Sprintf("TsDB2 database · created %s", time.Now().Format(time.RFC3339)))
+		// 建库: 未提供介绍 → 头页 Comment 默认写入 VersionComment
+		// (当前版本的功能介绍与修复点); 建库时间见 CreateTime 字段
+		header, err = newHeader("")
 		if err != nil {
 			return nil, err
 		}
@@ -373,7 +375,7 @@ func (db *DB) SetComment(desc string) error {
 		return fmt.Errorf("描述字符串长度 %d 超过最大限制 %d", len(desc), len(db.header.Comment))
 	}
 	db.file.withHeader(func() {
-		var comment [512]byte
+		var comment [PageSize4K - 332]byte
 		copy(comment[:], desc)
 		db.header.Comment = comment
 		db.header.ModifyTime = time.Now().UnixNano()

+ 17 - 9
DB/core/header_comment_test.go

@@ -8,15 +8,19 @@ import (
 	"git.ycl.pub/yl32940/TsDB2/DB/core/types"
 )
 
-// TestHeaderCommentAutoIntro 新建库时 Comment 自动写入创建介绍, 非空且含 "TsDB2"。
+// TestHeaderCommentAutoIntro 新建库未提供介绍时, Comment 默认写入 VersionComment
+// (当前版本的功能介绍与修复点)。
 func TestHeaderCommentAutoIntro(t *testing.T) {
 	db := fixOpenDB(t)
 	c := db.Comment()
 	if c == "" {
 		t.Fatal("新建库的 Comment 不应为空")
 	}
-	if !strings.Contains(c, "TsDB2") {
-		t.Fatalf("新建库的 Comment %q 应含 \"TsDB2\"", c)
+	if c != VersionComment {
+		t.Fatalf("新建库的 Comment 应等于 VersionComment, 得到 %q", c)
+	}
+	if !strings.Contains(c, "v11") {
+		t.Fatalf("默认介绍应含版本号 %q", c[:min(64, len(c))])
 	}
 }
 
@@ -49,19 +53,23 @@ func TestHeaderCommentSetAndReopen(t *testing.T) {
 	}
 }
 
-// TestHeaderCommentTooLong 超过 512 字节的介绍必须报错, 而不是静默截断。
+// TestHeaderCommentTooLong 超过介绍区容量 (PageSize4K-844 = 3252 字节) 必须报错,
+// 而不是静默截断; 恰好 3252 字节允许。
 func TestHeaderCommentTooLong(t *testing.T) {
 	db := fixOpenDB(t)
 	before := db.Comment()
-	if err := db.SetComment(strings.Repeat("x", 513)); err == nil {
-		t.Fatal("SetComment(513 字节) 应报错")
+	const cap1 = PageSize4K - 332 // 介绍区实际容量 (3764B)
+	if err := db.SetComment(strings.Repeat("x", cap1+1)); err == nil {
+		t.Fatalf("SetComment(%d 字节) 应报错", cap1+1)
 	}
 	if c := db.Comment(); c != before {
 		t.Fatalf("超长写入失败后 Comment 被改写成 %q", c)
 	}
-	// 恰好 512 字节是允许的
-	if err := db.SetComment(strings.Repeat("x", 512)); err != nil {
-		t.Fatalf("SetComment(512 字节) 不应报错: %v", err)
+	if err := db.SetComment(strings.Repeat("x", cap1)); err != nil {
+		t.Fatalf("SetComment(%d 字节, 恰好等于容量) 不应报错: %v", cap1, err)
+	}
+	if c := db.Comment(); len(c) != cap1 {
+		t.Fatalf("恰好容量的写入后长度 = %d, 想要 %d", len(c), cap1)
 	}
 }
 

+ 16 - 10
DB/core/header_page.go

@@ -18,7 +18,9 @@ import (
 //	[48,76)   表/空闲链/WAL: TablePage/TableCount/FreePageStart/FreePageEnd/
 //	          FreePageCount/FirstWALPage/LastWALPage (4 字节标量, 两两紧邻无填充)
 //	[76,332)  扩展: ExtConfig[256]
-//	[332,844) 介绍区: Comment[512]
+//	[332,4096) 介绍区: Comment[3764] — 占满头页余下全部空间 (建库默认写入
+//	          当前版本的功能介绍与修复点, 见 VersionComment; 用户可用
+//	          DB.SetComment 改写, 上限即本字段容量)
 //
 // 8 字节标量全部排在前半段 (偏移 16~48), 4 字节标量排在后半段 (偏移 48~76),
 // 任何字段后面都不留填充字节。
@@ -35,6 +37,8 @@ var (
 	_ [32]byte = [unsafe.Offsetof(HeaderPage{}.NextTxnID)]byte{}
 	_ [40]byte = [unsafe.Offsetof(HeaderPage{}.GlobalFlags)]byte{}
 	_ [332]byte = [unsafe.Offsetof(HeaderPage{}.Comment)]byte{}
+	// Comment 必须恰好占满头页剩余空间 (字段区 332 字节, 介绍区 3764 字节)。
+	_ [PageSize4K - 332]byte = [unsafe.Sizeof(HeaderPage{}.Comment)]byte{}
 )
 
 // 数据库全局头页(PageID=0 固定)
@@ -50,17 +54,15 @@ type HeaderPage struct {
 	TablePage     uint32    // 48   【唯一重要】表链起始页ID
 	TableCount    uint32    // 52   表数量
 	FreePageStart uint32    // 56   空闲页链表头
-	FreePageEnd   uint32    // 60   空闲页链表尾 (0 = 空闲链为空)
+	FreePageEnd   uint32    // 60   空闲页链表尾 (0 = 空闲链为空)。
+	                         // 不变量: FreePageStart == 0 当且仅当 FreePageEnd == 0,
+	                         // 两者必须同生同灭 (摘链/还链处维护)。
 	FreePageCount uint32    // 64   空闲页数量
 	FirstWALPage  uint32    // 68   未恢复的 WAL 链首页 ID (0=无待恢复WAL)
 	LastWALPage   uint32    // 72   WAL 链尾页 ID (追加写入用)
-	ExtConfig     [256]byte // 76   扩展配置
-	Comment       [512]byte // 332  数据库介绍区 (建库时自动写入, 见 DB.Comment/SetComment)
-
-	// 保留区, 填充对齐到整页。
-	// 不变量 (适用于 FreePageStart/FreePageEnd):
-	// FreePageStart == 0 当且仅当 FreePageEnd == 0, 两者必须同生同灭。
-	_ [PageSize4K - 844]byte
+	ExtConfig [256]byte         // 76   扩展配置
+	Comment   [PageSize4K - 332]byte // 332  数据库介绍区 (3764B, 占满整页余下空间;
+	                              // 建库默认写入 VersionComment, 可用 DB.SetComment 改写)
 }
 
 // newPageWithReader 分配新页:优先从空闲链表头取, 否则递增分配。
@@ -197,7 +199,11 @@ func recycleChainWithCache(ff *File, header *HeaderPage, head, tail, count uint3
 }
 
 func newHeader(desc string) (*HeaderPage, error) {
-	var comment [512]byte
+	// 未提供介绍 → 默认写入当前版本的功能介绍与修复点 (VersionComment)
+	if desc == "" {
+		desc = VersionComment
+	}
+	var comment [PageSize4K - 332]byte
 
 	// 1. 安全判断:防止描述超长
 	if len(desc) > len(comment) {

+ 8 - 6
README.md

@@ -351,7 +351,7 @@ func (db *DB) CoreDB() *core.DB       // 暴露底层 core,用于高级操作
 func (db *DB) DebugDBState()          // 打印 Header / BufferPool 调试信息
 func (db *DB) DebugTableState(tableName string)
 func (db *DB) Comment() string                    // 数据库介绍区内容(头页 Comment,建库自动写入)
-func (db *DB) SetComment(desc string) error       // 改写介绍区(≤512 字节,超长报错)
+func (db *DB) SetComment(desc string) error       // 改写介绍区(≤3764 字节,超长报错)
 
 // 获取(或创建)泛型表;表名缺省时由类型名推导(首个字母小写:Employee -> employee, OrderItem -> orderItem)
 func UseTable[T any](db *DB, name ...string) (*Table[T], error)
@@ -676,9 +676,10 @@ type HeaderPage struct {   // 4096 字节 (PageSize4K),字段顺序被对齐
 	FreePageCount uint32    // off 64   空闲页数量
 	FirstWALPage  uint32    // off 68   未恢复的 WAL 链首页(0 = 无待恢复 WAL)
 	LastWALPage   uint32    // off 72   WAL 链尾页(追加写入用)
-	ExtConfig     [256]byte // off 76   扩展配置
-	Comment       [512]byte // off 332  数据库介绍区(建库自动写入,`Comment()` / `SetComment()` 读写)
-	_             [3252]byte // off 844  保留区,补齐整页
+	ExtConfig     [256]byte         // off 76   扩展配置
+	Comment       [PageSize4K-332]byte // off 332  数据库介绍区 (3252B, 占满整页余下空间;
+	                                // 建库默认写入 VersionComment 版本介绍与修复点,
+	                                // `Comment()` / `SetComment()` 读写, 上限即本字段容量)
 }
 ```
 
@@ -711,8 +712,9 @@ type HeaderPage struct {   // 4096 字节 (PageSize4K),字段顺序被对齐
   从单堆页 ~16KB 扩到链页总 ≈4GB;Update / Delete / DropTable 自动释放旧 blob 链。旧库 blob 列的
   6B 引用会被误读成 BlobRef 高 6B(TotalLen 异常),不兼容需重建任何带 Blob 列的表。
 - `v11`(当前):HeaderPage 字段重排 —— `FreePageEnd` 从 Comment 之后(偏移 840)移到
-  `FreePageStart` 之后(偏移 60),字段按 8 字节对齐、零对齐填充字节;`Comment` 启用为数据库介绍区
-  (建库自动写入,`Comment()` / `SetComment()` 读写)。所有字段偏移变化,旧库不兼容需重建。
+  `FreePageStart` 之后(偏移 60),字段按 8 字节对齐、零对齐填充字节;`Comment` 扩展为
+  3252 字节介绍区(占满整页余下空间)——建库默认写入 `VersionComment`(当前版本的功能
+  介绍与修复点),`Comment()` / `SetComment()` 读写。所有字段偏移变化,旧库不兼容需重建。
 
 ### 页大小与公共页头
 

+ 1 - 1
analyzer/main.go

@@ -1078,7 +1078,7 @@ func rHdrInfo() *HdrInfo {
 	//   16 CreateTime | 24 ModifyTime | 32 NextTxnID | 40 GlobalFlags
 	//   48 TablePage | 52 TableCount | 56 FreePageStart | 60 FreePageEnd
 	//   64 FreePageCount | 68 FirstWALPage | 72 LastWALPage
-	//   76 ExtConfig[256] | 332 Comment[512]
+	//   76 ExtConfig[256] | 332 Comment[3764] (占满整页余下空间)
 	magic := string(b[:4])
 	ver := binary.LittleEndian.Uint32(b[4:8])
 	pgsz := binary.LittleEndian.Uint32(b[8:12])