package DB import ( "path/filepath" "strings" "testing" "git.ycl.pub/yl32940/TsDB2/DB/core/types" ) // TestQueryPropagatesRowConversionError 回归测试: 某行反序列化失败时, // FindAll / Select / Query.Scan 必须返回错误, 而不是静默跳过该行、 // 返回"看起来成功"的部分结果。 // // 触发方式: 用 core 层直接写入一行指向不存在链的 blob 引用 // (FirstPageID=0 且 TotalLen>0 → 读链必失败), 再经 ORM 全表扫描读到它。 func TestQueryPropagatesRowConversionError(t *testing.T) { type badBlob struct { BaseModel Data []byte `vrtx:"type:blob"` } db, err := Open(filepath.Join(t.TempDir(), "test.db")) if err != nil { t.Fatalf("open: %v", err) } defer db.Close() tbl, err := UseTable[badBlob](db, "badblob") if err != nil { t.Fatalf("use table: %v", err) } // 一行正常数据 if err := tbl.Insert(&badBlob{Data: []byte("hello")}); err != nil { t.Fatalf("insert good row: %v", err) } // 一行坏数据: 行内 blob ref 指向文件之外的页号 (core 层对 types.BlobRef 原样透传), // 读链必然失败 coreTbl, ok := db.CoreDB().FindTable("badblob") if !ok { t.Fatalf("core table not found") } if _, err := coreTbl.InsertRowWithID(999, []types.FieldValue{ {FieldName: []byte("Data"), Value: types.BlobRef{TotalLen: 10, FirstPageID: 0x00FFFFFF}}, }); err != nil { t.Fatalf("insert bad row: %v", err) } if err := db.Commit(); err != nil { t.Fatalf("commit: %v", err) } // FindAll: 旧实现会静默跳过坏行, 只返回 1 行且 err == nil rows, err := tbl.FindAll() if err == nil { t.Fatalf("FindAll 应返回反序列化错误, 却返回了 %d 行 nil error", len(rows)) } if !strings.Contains(err.Error(), "blob") { t.Fatalf("错误信息应说明 blob 读取失败, 得到: %v", err) } // Select: 同样必须报错而不是跳过 selErr := tbl.Select(func(*badBlob) bool { return true }) if selErr == nil { t.Fatalf("Select 应返回反序列化错误") } // Query.Scan: 同样必须报错而不是跳过 scanErr := tbl.Query().Scan(func(*badBlob) bool { return true }) if scanErr == nil { t.Fatalf("Scan 应返回反序列化错误") } // 坏行之前的正常行不受影响: 主键点查仍应拿到正确内容 got, err := tbl.Find(1) if err != nil { t.Fatalf("Find good row: %v", err) } if string(got.Data) != "hello" { t.Fatalf("good row Data = %q, 想要 hello", got.Data) } }