Sqlite_exec callback function in Swift
This is about the sqlite_exec(…) callback function in Swift and double indirection operators
The code presented here shows a test program that passes SQL statements to Sqlite. The code was pieced together from examples found online and from error messages that emerged.
At the top is some C++ code that shows a working callback function. In that code we see that the double indirection operators for **argv and **azColName.
The call to sqlite3_exec(db, sql, nil , nil , nil ) works because it does not include the callback function. The call to sqlite3_exec(db, sql, callback, nil , nil ) does not work because there are errors in the callback function.
Based on the Swift error messages, I derived the following parameter in the callback function. It is one of two that return character strings. the Optional comes from error messages that suggested this format, and I find that this code does not return error messages.
argv: Optional<UnsafeMutablePointer<Optional<UnsafeMutablePointer>>>
As I see it, this line shows a double pointer, where the C++ program suggests double indirection (deference).
In Swift, one can dereference a single pointer with
s2 = argv!.pointee
However, I cannot find any way to dereference a double pointer and recover the string value that is in an array. How can I make this work in Swift? And then there’s the problem that the callback returns an array of character strings.
This is my first post to a Swift forum. I'm not seeing a way to specifically format the following text as code so may need advice on how to do that.
// Swift_Sqlite_Experiment.xcodeproj
import Foundation
import SQLite3
/*
Example C++ callback function from sqlite3_exec
Note the double indirection operators for **argv and **azColName
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
int i;
for(i=0; i<argc; i++)
{
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
*/
// Test Swift callback function that was pieced together from error messages
// Note the double pointers that were the result of error resolution
func callback(
NotUsed: Optional<UnsafeMutableRawPointer>,
argc: Int32,
argv: Optional<UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>>,
azColName: Optional<UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>> ) -> Int32
{
for i in 0 ... Int(argc)
{
// Need to recover strings from pointers, below.
var s1: String
s1 = azColName!.pointee // See error message for text
var s2: String
s2 = argv!.pointee // See error message for text
print("%i %s %s \n", i, s1, s2 ) //
}
// Error Cannot assign value of type 'Optional<UnsafeMutalePointer<Int8>>' to type 'Int8'
return 0
}
// Path to test.db file
let file1 = "XcodeProgramFiles/Swift_Sqlite_Experiment/Swift_Sqlite_Experiment/test.db"
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent(file1)
print(fileURL)
var db: OpaquePointer?
sqlite3_open(fileURL.path, &db )
if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
print("error opening database")
}
var sql = "CREATE TABLE COMPANY("
sql += "ID INT PRIMARY KEY NOT NULL,"
sql += "NAME TEXT NOT NULL,"
sql += "AGE INT NOT NULL,"
sql += "ADDRESS CHAR(50),"
sql += "SALARY REAL );";
// Creating table
if sqlite3_exec(db, sql, nil, nil, nil) != SQLITE_OK {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error creating table: \(errmsg)")
} else
{
print( "Table created -- check")
}
sql = "SELECT * from COMPANY WHERE ID>0;";
var s1 = ""
var a = 5 // was nil
func x(){}
// Need an adequate example of the settings for sqlite3_exec that are shown here as nil
// This works but needs a callback function
if sqlite3_exec(db, sql, nil, nil, nil) != SQLITE_OK {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error -->: \(errmsg)")
}
else
{
print( "Exec Command Completed")
}
// This uses the callback function shown above
if sqlite3_exec(db, sql, callback, nil, nil) != SQLITE_OK {
let errmsg = String(cString: sqlite3_errmsg(db)!)
print("error -->: \(errmsg)")
}
else
{
print( "Exec Command Completed")
}
sqlite3_close(db)
print("Done")