Upgrading to SQLCipher 4


Hello @sjlombardo. Can you please clarify how to make migration when custom SQLCipher settings were used. Specifically, my application uses PRAGMA cipher_page_size=4096 and PRAGMA page_size=4096. Can I use PRAGMA cipher_migrate or have to fall down to sqlcipher_export?


Hi @diejmon

No, you cannot use PRAGMA cipher_migrate when non-default configurations were used. You can however use sqlcipher_export(...). For your situation, please review example #3 on the documentation here. In your case, the difference will be the cipher_page_size being different from the example.


I found that PRAGMA cipher_migrate only upgrades the database file format, all data are lost, did I make something wrong?


Hi @missdeer

In my case, after exec “PRAGMA cipher_migrate”, database data are kept(i.e. no data lost).

Maybe you should share more details which can help sqlcipher team to support your issue.



Hi @missdeer

While you cannot use PRAGMA cipher_migrate when the database was previously configured with non-default settings, no data loss should occur. Please refer to these comments here:


@developernotes @hwbest
My fault.
I found that PRAGMA cipher_migrate returned 0, but in fact it didn’t migrate successfully, it only can be open and read/write with old SQLCipher 3 settings rather than new SQLCipher 4 settings.

My code:

do {
    rc = sqlite3_open_v2(origPath.c_str(), &m_db, SQLITE_OPEN_READWRITE, nullptr);
    if (rc != SQLITE_OK)
        logErrorS("opening database failed: " << origPath << rc << sqlite3_errmsg(m_db));
    rc = sqlite3_key_v2(m_db, nullptr, keyReader.c_str(), keyReader.getBufSize());
    if (rc != SQLITE_OK)
        logErrorS("attach encrypt key failed: " << origPath << rc << sqlite3_errmsg(m_db));
    auto resultPtr = execStatement(SQL_STATEMENT_CIPHER_MIGRATE, DBParamSetPtr(), true);
    if (!resultPtr || !resultPtr->size())
        logErrorS("cipher_migrate doesn't returns result: " << origPath << sqlite3_errmsg(m_db));
    DBRowPtr row = (*resultPtr)[0];
    DBColumnPtr column = (*row)["cipher_migrate"];
    if (!column)
        logErrorS("cipher_migrate doesn't returns cipher_migrate field: " << origPath << sqlite3_errmsg(m_db));
    int res = column->getInt32Value();
    if (res)
        logErrorS("cipher_migrate failed: " << origPath << sqlite3_errmsg(m_db));
    openFlag = true;
} while (0);


Hi @sjlombardo @developernotes ,
I am planing to take option 2 to be backward compatible. I would like to know is there any security vulnerabilities or performance implication with that approach.
I would like to take this option by checking version of DB by using 'PRAGMA user_version;" which returns 0 because there is no version set by creator of DB or select sqlite_version(); which returns 3 as major version. In any of these cases, will set PRAGMA cipher_compatibility = 3; and use it otherwise will use directly. Please let me know your views on this.


Hi @krishnakukmartm - There aren’t any specific vulnerabilities involved with using option 2 for backward compatibility. Note however that using backwards compatibility is less secure, due to the security enhancements in version 4 that would not be in place.

The approach you have described with user_version and sqlite_version won’t really work. Querying the user_version would require the database to be decrypted, and the compatibility flag would have to be set before decryption. With sqlite_version, it returns the version of the library being used, not the version of the database. One recommended approach for determining the database version is described earlier in this thread:

This example refers to cipher_migrate, but the same approach could conceivably be used for compatibility.

split this topic #33

A post was split to a new topic: SQLCipher 4 and REAL data type retrieval