Я хочу создать функцию, которая выполняет sqlite3_exec и сохраняет результаты в объект словаря, который передается в качестве параметра. Для этого я создал следующий код:
func execute(sql: String, result: inout Dictionary<String, String>) {
let rc = sqlite3_exec(
dbPointer, // database
sql, // statement
{ // callback: non-capturing closure
resultVoidPointer, columnCount, values, columns in
for i in 0 ..< Int(columnCount) {
let value = String(cString: values![i]!)
let column = String(cString: columns![i]!)
result[column] = value // Error: Escaping closures can only capture inout parameters explicitly by value
print("\(column) = \(value)")
}
return 0
},
nil,
nil
)
if rc != SQLITE_OK {
print("SQLite Error: " + errorMessage)
}
}
Но этот код дает мне следующую ошибку: Escaping closures can only capture inout parameters explicitly by value. Другая попытка состоит в том, чтобы функция execute возвращала объект словаря, но тогда сообщение об ошибке становится таким: A C function pointer cannot be formed from a closure that captures context. Как я могу это исправить?
Я думал, что это будет более простым решением. Не могли бы вы предоставить код, в котором sqlite3_exec() заменен на prepare, step и finalize, чтобы моя функция execute могла принимать произвольные операторы sql и создавать словарь со столбцом: значение в качестве пары ключ: значение?
Я не знаю о какой-либо документации по Swift, потому что я никогда не использовал этот язык, но соответствие с функциями C, похоже, в основном один к одному, и этот рабочий процесс и соответствующие функции описаны здесь: sqlite.org/c3ref/stmt.html
Кроме того, то, что вы делаете, на самом деле не имеет смысла ... Если бы это сработало, вы бы перезаписывали одни и те же записи в словаре каждой строкой и заканчивали бы только значениями последних строк. Кажется маловероятным, что вы хотите, если ваш запрос не возвращает только одну строку.
Да тоже только что понял. Мне, вероятно, понадобится массив словарей, куда я добавляю новый элемент словаря каждый раз, когда вызывается обратный вызов.





Следуя комментариям Шон, я создал новую версию execute, где я использую sqlite3_prepare_v2, sqlite3_step и sqlite3_finalize вместо комбинированной функции sqlite3_exec:
func prepare(sql: String) -> OpaquePointer? {
var statement: OpaquePointer? = nil
guard sqlite3_prepare_v2(dbPointer, sql, -1, &statement, nil) == SQLITE_OK else {
print(errorMessage)
return nil
}
return statement
}
func execute(sql: String) -> [Dictionary<String, String>] {
var result: [Dictionary<String, String>] = []
guard let queryStatement = prepare(sql: sql) else {
return result
}
defer {
sqlite3_finalize(queryStatement)
}
while sqlite3_step(queryStatement) == SQLITE_ROW {
var dictionary: Dictionary<String, String> = [:]
for i in 0...sqlite3_column_count(queryStatement)-1 {
let column: String = String(cString: sqlite3_column_name(queryStatement, i))
let value: String = String(cString: sqlite3_column_text(queryStatement, i))
dictionary[column] = value
}
result.append(dictionary)
}
guard sqlite3_step(queryStatement) == SQLITE_DONE else {
print(errorMessage)
return result
}
return result
}
Моя функция execute теперь возвращает массив словарей, где каждый словарь представляет собой строку для операторов SQL, таких как SELECT. Для других операторов SQL, таких как INSERT, UPDATE или DELETE, где строки не возвращаются, функция execute возвращает пустой массив.
После выполнения оператора SQL к результату можно получить доступ с помощью
let result = execute(db, "SELECT id, name, age FROM Table")
print(result[i][age]) // prints the value in row i and column age
Эр. Почему бы не использовать обычный подготовленный API и цикл, пока
sqlite3_step()возвращаетSQLITE_ROW? Вот как вы это сделаете в C, и привязки Swift почти идентичны тем, что я видел.