|
@@ -377,13 +377,44 @@ func (db *DB) vacuumScan(progress VACUUMProgress) ([]vacuumSrcTable, int64, erro
|
|
|
|
|
|
|
|
// 行快照 (保留 NULL: deserializeRow 会跳过 NULL 列, VACUUM 会把
|
|
// 行快照 (保留 NULL: deserializeRow 会跳过 NULL 列, VACUUM 会把
|
|
|
// NULL 物化成类型零值 —— 语义漂移且不可逆)。
|
|
// NULL 物化成类型零值 —— 语义漂移且不可逆)。
|
|
|
|
|
+ // ⭐ Blob 列特判: deserialize 产出的是 8B BlobRef (指向【本文件】的链页),
|
|
|
|
|
+ // 直接搬过去新库后链页并不存在 —— 读回 EOF/空 (DockerManager 实测:
|
|
|
|
|
+ // 迁移+VACUUM 后 param 列全崩)。必须在扫描时把链内容物化进 vals,
|
|
|
|
|
+ // 让新库的序列化走 blobFieldRef 落【新链】。
|
|
|
|
|
+ blobCols := map[string]bool{}
|
|
|
|
|
+ for _, f := range fields {
|
|
|
|
|
+ if types.IsBlob(f.FieldType) {
|
|
|
|
|
+ blobCols[string(f.FieldName)] = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
rows := make([]vacuumSrcRow, 0, tbl.TotalRows())
|
|
rows := make([]vacuumSrcRow, 0, tbl.TotalRows())
|
|
|
q := tbl.Query()
|
|
q := tbl.Query()
|
|
|
|
|
+ var blobErr error // 闭包内物化 Blob 的失败载体 (闭包不能引用后声明的 scanErr)
|
|
|
scanErr := q.fullTableScanRaw(func(pkID uint64, rowData []byte) bool {
|
|
scanErr := q.fullTableScanRaw(func(pkID uint64, rowData []byte) bool {
|
|
|
vals := tbl.deserializeRowKeepNull(rowData)
|
|
vals := tbl.deserializeRowKeepNull(rowData)
|
|
|
if vals == nil {
|
|
if vals == nil {
|
|
|
return true // 解析失败, 跳过
|
|
return true // 解析失败, 跳过
|
|
|
}
|
|
}
|
|
|
|
|
+ if len(blobCols) > 0 {
|
|
|
|
|
+ for i, fv := range vals {
|
|
|
|
|
+ if !blobCols[string(fv.FieldName)] {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ ref, ok := fv.Value.(types.BlobRef)
|
|
|
|
|
+ if !ok || ref.Empty() {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ data, err := tbl.BlobRead(ref, types.CompressNone) // 写侧约定 CompressNone
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ blobErr = fmt.Errorf("物化 Blob 列 %q (pkID=%d): %w", fv.FieldName, pkID, err)
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ vals[i].Value = data
|
|
|
|
|
+ }
|
|
|
|
|
+ if blobErr != nil {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
rows = append(rows, vacuumSrcRow{pkID: pkID, vals: vals})
|
|
rows = append(rows, vacuumSrcRow{pkID: pkID, vals: vals})
|
|
|
scanned++
|
|
scanned++
|
|
|
if scanned%10000 == 0 {
|
|
if scanned%10000 == 0 {
|
|
@@ -391,6 +422,9 @@ func (db *DB) vacuumScan(progress VACUUMProgress) ([]vacuumSrcTable, int64, erro
|
|
|
}
|
|
}
|
|
|
return true
|
|
return true
|
|
|
})
|
|
})
|
|
|
|
|
+ if blobErr != nil {
|
|
|
|
|
+ return nil, 0, blobErr
|
|
|
|
|
+ }
|
|
|
if scanErr != nil {
|
|
if scanErr != nil {
|
|
|
return nil, 0, fmt.Errorf("扫描表 %q: %w", meta.Name, scanErr)
|
|
return nil, 0, fmt.Errorf("扫描表 %q: %w", meta.Name, scanErr)
|
|
|
}
|
|
}
|