#1545·wcdb

Swift-数据迁移有BUG

Author: xiaofeng886Created May 9, 2026Updated Jul 1, 2026

在一个未做加密的数据库上,需要进行数据迁移,迁移到一个加密的数据库上,代码如下:

let oldDBPath = MyDBDataPath.basePath + userId + ".db"
     let newDBPath = MyDBDataPath.basePath + userId + "_encryption.db"

     let oldDataBase = Database(at: oldDBPath)
     self.createTable(dataBase: oldDataBase, tableSubString: "")

     self.myDatabase = Database(at: newDBPath)
     let password = userId.data(using: .ascii)
     self.myDatabase?.setCipher(key: password)
     self.myDatabase?.setAutoMigration(enable: true)
//        self.myDatabase?.tag = 10599
     self.myDatabase?.addMigration(sourcePath: oldDBPath,{ info in
         info.sourceTable = info.table + "_encryption"; //配置数据迁移的源表格名
     })
     self.createTable(dataBase: self.myDatabase, tableSubString: "_encryption")

在迁移的过程中报错: "未能完成操作。(WCDBSwift.WCDBError错误1。)"

使用AI进行分析如下:

根本原因分析 通过深入分析 WCDB 源码,我找到了确切的 bug 原因: SQLCipher ATTACH 机制问题 从 MigrationInfo.cpp 可以看到:

MigrationDatabaseInfo::MigrationDatabaseInfo(...) {
    m_statementForAttachingSchema = StatementAttach().attach(m_sourcePath).as(m_schema);
    if (!cipher.empty()) {
        m_statementForAttachingSchema.key(BindParameter(1)); // 只有cipher非空才添加KEY
    }
}

再看 MigratingHandleDecorator.cpp 的 attach 逻辑:

bool MigratingHandleDecorator::attachDatabase(...) {
    const Data& cipher = attachInfo->getSourceCipher();
    if (!cipher.empty()) {
        handleStatement.bindBLOB(cipher); // 只有cipher非空才绑定key
    }
    succeed = handleStatement.step();
}

而 UnsafeData::empty() 判断逻辑是 m_size == 0。问题链条: 1、你传 sourceCipher: nil → C++ 层 cipher 为空 (size=0) 2、ATTACH 语句变成:ATTACH DATABASE '/old.db' AS 'schema'(没有 KEY 子句) 3、SQLCipher 规则:ATTACH 不指定 KEY 时,使用主数据库的 key 来打开附加数据库 4、你的主数据库(target)是加密的,SQLCipher 用这个密钥去解密未加密的源数据库 → 失败!

正确的 SQL 应该是:

ATTACH DATABASE '/old.db' AS 'schema' KEY ''; -- 空key表示明文

这是 WCDB 2.1.15 版本在"加密目标 + 未加密源"跨库迁移场景下的 bug。