导出:删掉到访后,别把它指向的原始采样点留成悬空引用
真机报「备份恢复失败,你的当前数据未被更改」——这句说明它是先校验、整体回滚。 往下查的第一个醒悟:**Core Data 的 SQLite 表里根本没有外键约束**(关系由 Core Data 自己维护),所以之前跑的 PRAGMA foreign_key_check 报 0 违例毫无意义,它压根没得可查。 按关系列手查悬空引用,结果: 原版:悬空 0 导出:ZRAWVISIT.ZVISIT 有 2 条指向不存在的 ZVISIT —— (2051→到访46, 465→到访200) 正是用户在站点上删掉的那两条到访。ZRAWVISIT(原始 GPS 采样点)是不托管实体、 导出时原样保留,于是删到访就留下了指向已删记录的悬空关系;而真机删到访时 采样点是跟着级联删的,所以这是它自己绝不会产生的形状。 修法:visit.finish() 之后,删掉 ZVISIT 指向不存在到访的 ZRAWVISIT 行。 选「删」而不是「把 ZVISIT 置空」:真机的「未归并采样点」虽有 1461/2246 条(置空合法), 但留成未归并会让 app 重新推出一条到访,把用户删掉的记录复活。 顺带加固: - verify_export_structure.py 新增 1.45 节「关系完整性」,手查 8 组关系列的悬空引用, 有悬空就把结论判成「有差异」——这条 foreign_key_check 查不出来的坑进了标准验收清单。 - TestExportDropsOrphanedRawVisits:删一条带采样点的到访 → 导出 → 断言没有悬空;已做变异验证。
This commit is contained in:
@@ -460,6 +460,14 @@ func patchSQLite(ctx context.Context, db *sql.DB, datasetID int64, sqlitePath st
|
||||
if err := visit.finish(); err != nil {
|
||||
return err
|
||||
}
|
||||
// 站点删掉到访之后,它的原始采样点会失去目标。ZRAWVISIT 是不托管实体(原样保留),
|
||||
// 而 Core Data 的关系由它自己维护、SQLite 里没有外键约束,所以这种悬空引用不会报错,
|
||||
// 但它是**真机自己绝不会产生的形状**——真机删到访时采样点跟着级联删。
|
||||
// 留着还有副作用:app 会把「没归并的采样点」重新推成一条到访,删掉的记录又回来了。
|
||||
if _, err := tx.ExecContext(ctx, `DELETE FROM ZRAWVISIT WHERE ZVISIT IS NOT NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM ZVISIT v WHERE v.Z_PK = ZRAWVISIT.ZVISIT)`); err != nil {
|
||||
return fmt.Errorf("清理失去目标的原始采样点失败: %w", err)
|
||||
}
|
||||
|
||||
// 8) 访问↔标签关联(列名从基线探测得到;纯两列关系,全量重写即可)
|
||||
if join.Name != "" && join.VisitCol != "" && join.TagCol != "" {
|
||||
|
||||
Reference in New Issue
Block a user