Hi @forestluo
Thanks for providing the sample. I still wasn’t able to reproduce the issue in an example program using your swift code for stepping.
Sample code:
import SQLCipher
enum SQLCipherError: Error {
case openFailure
case keyFailure
case createTableFailure
case prepareFailure
case bindFailure
case stepFailure
}
struct SQLCipherManager {
static func testSQLCipherColumnBytes(_ charCount: Int32) throws {
var rc: Int32
var db: OpaquePointer? = nil
var stmt: OpaquePointer? = nil
let password: String = "correct horse battery staple"
var thrownError: Error?
do {
guard let dbPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("sqlcipher.db").path() else {
throw SQLCipherError.openFailure
}
rc = sqlite3_open(dbPath, &db)
guard rc == SQLITE_OK else {
let errMsg = String(cString: sqlite3_errmsg(db))
print("sqlite3 error \(errMsg)")
throw SQLCipherError.openFailure
}
rc = sqlite3_key(db, password, Int32(password.utf8CString.count))
guard rc == SQLITE_OK else {
throw SQLCipherError.keyFailure
}
rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS datatables(id INTEGER PRIMARY KEY AUTOINCREMENT, header TEXT, databody TEXT, remark TEXT);", nil, nil, nil)
guard rc == SQLITE_OK else {
throw SQLCipherError.createTableFailure
}
rc = sqlite3_prepare(db, "INSERT INTO datatables(header, databody, remark) VALUES(?,?,?) returning id;", -1, &stmt, nil)
guard rc == SQLITE_OK else {
throw SQLCipherError.prepareFailure
}
let repeatedCString = String(repeating: "a", count: Int(charCount)).withCString { cString in
return cString
}
// only use the exact char count without null termination
rc = sqlite3_bind_text(stmt, 1, repeatedCString, charCount, SQLITE_TRANSIENT)
guard rc == SQLITE_OK else {
throw SQLCipherError.bindFailure
}
rc = sqlite3_bind_text(stmt, 2, repeatedCString, charCount, SQLITE_TRANSIENT)
guard rc == SQLITE_OK else {
throw SQLCipherError.bindFailure
}
rc = sqlite3_bind_text(stmt, 3, repeatedCString, charCount, SQLITE_TRANSIENT)
guard rc == SQLITE_OK else {
throw SQLCipherError.bindFailure
}
var id: Int32
if sqlite3_step(stmt) == SQLITE_ROW {
id = sqlite3_column_int(stmt, 0)
} else {
throw SQLCipherError.stepFailure
}
sqlite3_finalize(stmt)
rc = sqlite3_prepare(db, "SELECT header, databody, remark FROM datatables WHERE id = ?;", -1, &stmt, nil)
guard rc == SQLITE_OK else {
throw SQLCipherError.prepareFailure
}
rc = sqlite3_bind_int(stmt, 1, id)
while sqlite3_step(stmt) == SQLITE_ROW {
let columns: [String?] = (0...2).map { index in
let len = sqlite3_column_bytes(stmt, Int32(index))
print("length = \(len)")
if len > 0,
let ptr = sqlite3_column_blob(stmt, Int32(len)) {
let data = Data(bytes: ptr, count: Int(len))
return String(data: data, encoding: .utf8)
} else { return nil }
}
}
sqlite3_finalize(stmt)
rc = sqlite3_prepare(db, "SELECT octet_length(header), octet_length(databody), octet_length(remark) FROM datatables WHERE id = ?;", -1, &stmt, nil)
guard rc == SQLITE_OK else {
throw SQLCipherError.prepareFailure
}
rc = sqlite3_bind_int(stmt, 1, id)
while sqlite3_step(stmt) == SQLITE_ROW {
let vals: [Int32?] = (0...2).map { index in
let val = sqlite3_column_int(stmt, Int32(index))
print("octet_length = \(val)")
return val
}
}
} catch {
thrownError = error
}
if stmt != nil {
sqlite3_finalize(stmt)
}
if db != nil {
sqlite3_close(db)
}
if let err = thrownError {
throw err
}
}
}
Caller example:
try SQLCipherManager.testSQLCipherColumnBytes(4096)
Which prints out:
length = 4096
length = 4096
length = 4096
octet_length = 4096
octet_length = 4096
octet_length = 4096
A few other questions:
- Could you try running this query in your project on your data to see what it returns for the octet_length:
"SELECT octet_length(header), octet_length(databody), octet_length(remark) FROM datatables WHERE id = ?;"
- What does your table schema look like? Can you show the sample code of how you’re inserting the data?
- Are you able to provide a non-sensitive example of column content which reproduces the issue?
- Can you try the sample function above in your project and see if it produces the expected result?
- Are you able to reproduce the issue in a standalone sample project with only a SQLCipher depdendency (no other sqlite dependencies)? If so can you share the sample project?