I am using sqlite in an iOS project. However, sqlite3_column_count always returns 0, although I'm sure the query returns some columns (I donβt even think that SELECT query in SQL is possible with zero output columns). The strange thing is a change in behavior depending on the version of the library I am referring to, and, in particular, what compilation time parameters were used for this library.
These are the compilation time parameters used for the version of the library that does not work:
COMPILER=clang-8.0.0 (clang-800.2.34) ENABLE_API_ARMOR ENABLE_FTS3 ENABLE_FTS3_PARENTHESIS ENABLE_JSON1 ENABLE_LOCKING_STYLE=1 ENABLE_RTREE ENABLE_UPDATE_DELETE_LIMIT HAS_CODEC HAVE_ISNAN MAX_MMAP_SIZE=20971520 OMIT_AUTORESET OMIT_BUILTIN_TEST OMIT_LOAD_EXTENSION SYSTEM_MALLOC THREADSAFE=2
Here are the flags for the version that works:
COMPILER=clang-8.0.0 SYSTEM_MALLOC THREADSAFE=1
What could be causing this behavior?
UPDATE 2016/12/01
Here is a project that can be used to recreate the problem: https://github.com/iannewson/Stackoverflow40895740.git
ViewController.swift contains this code:
Test.doDbStuff()
This function contains the following:
public static func doDbStuff() { do { var db :OpaquePointer? let dbPath = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) .appendingPathComponent("\(Date.init().timeIntervalSince1970)".replacingOccurrences(of: ".", with: "") + ".db") .path var returnCode :Int32 = sqlite3_open(dbPath, &db) if SQLITE_OK != returnCode { preconditionFailure("Failed to open db") } var stmt :OpaquePointer? returnCode = sqlite3_prepare_v2(db, "CREATE TABLE Things (name TEXT)", -1, &stmt, nil) if SQLITE_OK != returnCode { preconditionFailure("Failed to prepare table creation SQL") } returnCode = sqlite3_step(stmt) if SQLITE_DONE != returnCode { preconditionFailure("Failed to execute tbl creation SQL") } returnCode = sqlite3_prepare_v2(db, "SELECT name FROM sqlite_master WHERE type ='table'", -1, &stmt, nil) if SQLITE_OK != returnCode { preconditionFailure("Failed to prepare count SQL") } returnCode = sqlite3_step(stmt) if SQLITE_ROW != returnCode { preconditionFailure("Failed to execute count SQL") } else { let columnCount = sqlite3_column_count(stmt) print("columnCount: \(columnCount)") let name = String(cString: sqlite3_column_text(stmt, 0)) //print("Num tables: \(count)") print("Table name: \(name)") } } catch let error { preconditionFailure(error.localizedDescription) } }
This function works fine in all cases and always creates a table and retrieves its name from sqlite_master without errors.
ViewController.swift also contains this code:
Database.instance().executeSql("CREATE TABLE IF NOT EXISTS Things (name TEXT)") Database.instance().printTableNames() Database.instance().withStatementFromSql("SELECT name FROM sqlite_master WHERE type ='table'", callback: {statement in while SQLITE_ROW == sqlite3_step(statement) { print("Column index: \(sqlite3_column_count(statement))") let name = SqlHelper.toString(statement, columnName: "name") print("Table: \(name)") } })
This code creates a table and then tries to get its name from sqlite_master. This works great with the project currently configured in the repo, so you need to install the Google Analytics module to reproduce the error. You can do this by uncommenting the pod 'Google/Analytics' in the subfile and running pod install . Now, when you try to run this project, the first set of code ( Test.doDbStuff() ) will still work fine, but the second set of code that Database uses will either be with EXC_BAD_ACCESS or return SQLITE_MISUSE from sqlite3_step .