You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

sqflite_sqlcipher 2.2.1以上版本打开SQLCipher3加密DB时内存不足崩溃

问题描述

使用sqflite_sqlcipher 2.2.1及以上版本时,打开SQLCipher3加密的数据库会崩溃,相同代码在2.2.1版本可正常运行。

代码片段

Future<Database> _open(String databasePath, String key) async {
  try {
    if (key.isEmpty) {
      return await openDatabase(databasePath, version: 1);
    } else {
      return await openDatabase(databasePath, password: key, version: 1);
    }
  } catch (e) {
    print(
        "Error opening database normally: $e. Retrying with cipher compatibility...");
    
    return await openDatabase(
      databasePath,
      password: key,
      version: 1,
      onConfigure: (db) async {
        await db.execute('PRAGMA cipher_compatibility = 3;');
      },
    );
  }
}

错误日志

flutter: Error opening database normally: DatabaseException(open_failed ..../Documents/9781316644607.db). Retrying with cipher compatibility...
flutter: error DatabaseException(Error Domain=FMDatabase Code=7 "out of memory" UserInfo={NSLocalizedDescription=out of memory}) sql 'BEGIN EXCLUSIVE' args [] during open, closing...
[ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: DatabaseException(Error Domain=FMDatabase Code=7 "out of memory" UserInfo={NSLocalizedDescription=out of memory}) sql 'BEGIN EXCLUSIVE' args []
#0 SqfliteSqlCipherDatabaseFactoryImpl.wrapDatabaseException (package:sqflite_sqlcipher/src/factory_sql_cipher_impl.dart:44:9)
#1 SqfliteDatabaseMixin.txnSynchronized (package:sqflite_common/src/database_mixin.dart:554:16)
#2 SqfliteDatabaseMixinExt.txnBeginTransaction (package:sqflite_common/src/database_mixin.dart:411:20)
#3 SqfliteDatabaseMixin.beginTransaction (package:sqflite_common/src/database_mixin.dart:863:5)
#4 SqfliteDatabaseMixinExt._txnTransaction (package:sqflite_common/src/database_mixin.dart:384:13)
#5 BasicLock.synchronized (package:synchronized/src/basic_lock.dart:38:16)

观察结果

  • sqflite_sqlcipher 2.2.1版本可正常运行
  • 2.2.1以上所有版本均失败
  • 数据库采用SQLCipher3加密

解决方案

问题出在高版本sqflite_sqlcipher中,onConfigure回调的执行时机已发生变化——在数据库尝试建立连接并执行初始化事务之后才会触发,此时SQLCipher3的兼容性参数还未设置,导致数据库解密失败并抛出内存错误。

正确的做法是在数据库打开前就设置SQLCipher3的兼容参数,不要依赖onConfigure回调,改用customOpenStatement参数直接指定兼容配置:

Future<Database> _open(String databasePath, String key) async {
  if (key.isEmpty) {
    return await openDatabase(databasePath, version: 1);
  } else {
    try {
      return await openDatabase(databasePath, password: key, version: 1);
    } catch (e) {
      print(
          "Error opening database normally: $e. Retrying with cipher compatibility...");
      
      // 使用customOpenStatement在打开时直接设置兼容参数
      return await openDatabase(
        databasePath,
        password: key,
        version: 1,
        customOpenStatement: 'PRAGMA cipher_compatibility = 3;',
      );
    }
  }
}

原理说明

sqflite_sqlcipher 2.2.1之后的版本升级了底层SQLCipher库,默认加密参数与SQLCipher3不兼容。而customOpenStatement会在数据库连接建立后立即执行指定SQL语句,确保解密时使用正确的兼容配置,避免了onConfigure回调时机滞后带来的问题。


内容的提问来源于stack exchange,提问作者Aakash Deep

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.01 20:33:14