Sqlite для быстрого является неустойчивым

Я создал быстрый проект для использования sqlite. иногда при вставке он фактически не вставляет правильные (или все) значения. Я знаю, потому что я перезапускаю приложение, и когда я возвращаюсь в записи, они либо беспорядочны (с не вставленным материалом), либо ноль. но иногда правильно.

вот где я его устанавливаю, и да, данные верны в значениях перед вставкой.

let update = "INSERT INTO ToDoItem (itemName, completed, goalDate) " + "VALUES (?, ?, ?);"
var statement: COpaquePointer = nil
if sqlite3_prepare_v2(database, update, -1, &statement, nil) == SQLITE_OK {
    let itemName = item.itemName as String
    let completed = item.completed == true ? 1 : 0

    sqlite3_bind_text(statement, 1, itemName, -1, nil)
    sqlite3_bind_int(statement, 2, Int32(completed))

    if let goalDate = item.goalDate?.toString() {
        sqlite3_bind_text(statement, 3, goalDate, -1, nil)
    } else {
        sqlite3_bind_text(statement, 3, "", -1, nil)
    }

    //println("inserting \(itemName), \(completed) and \(item.goalDate?.toString())")
    //println("")
}

if sqlite3_step(statement) != SQLITE_DONE {
    println("error updateing table")
    sqlite3_close(database)
    return
}
sqlite3_finalize(statement)

sqlite3_close(database)

вы можете увидеть прокомментированный println посередине, если это не закомментировано, тогда itemName иногда получает часть этой строки.

Ответ 1

У меня такая же проблема. Я нашел способ решить эту проблему.

sqlite3_bind_text(statement, 1, itemName, -1, nil) --> itemName should be UTF8 String

Вы должны преобразовать itemName как NSString и использовать UTF8String для преобразования вашей строки в UTF8. Правильный код здесь такой же

let itemName = item.itemName as NSString
sqlite3_bind_text(statement, 1, itemName.UTF8String, -1, nil)

Удачи.

Ответ 2

В быстром SQLite не определены SQLITE_TRANSIENT и SQLITE_STATIC, поэтому мы должны явно его определить.

Свифт 3 и 4

Определите следующее свойство SQLITE

let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)

Функция SQL Добавьте SQLITE_TRANSIENT вместо nil качестве последнего параметра при привязке текста sqlite3_bind_text.

let update = "INSERT INTO ToDoItem (itemName, completed, goalDate) " + "VALUES (?, ?, ?);"
var statement: OpaquePointer = nil
if sqlite3_prepare_v2(database, update, -1, &statement, nil) == SQLITE_OK {
    let itemName = item.itemName as String
    let completed = item.completed == true ? 1 : 0

    sqlite3_bind_text(statement, 1, itemName, -1, SQLITE_TRANSIENT)
    sqlite3_bind_int(statement, 2, Int32(completed))

    if let goalDate = item.goalDate?.toString() {
        sqlite3_bind_text(statement, 3, goalDate, -1, SQLITE_TRANSIENT)
    } else {
        sqlite3_bind_text(statement, 3, "", -1, SQLITE_TRANSIENT)
    }

    //println("inserting \(itemName), \(completed) and \(item.goalDate?.toString())")
    //println("")
}

if sqlite3_step(statement) != SQLITE_DONE {
    println("error updateing table")
    sqlite3_close(database)
    return
}
sqlite3_finalize(statement)

sqlite3_close(database)

Ссылка на SQLITE_TRANSIENT не определена в Swift

Ответ 3

Это поведение действительно соответствует спецификации, а ошибка в вашем коде.

Источником вашей проблемы является быстрый String переход на sqlite3_bind_text. sqlite3_bind_text принимает текст как const char*, который соединяется как UnsafePointer<CChar> в Swift. Поведение при передаче Swift String функции, принимающей UnsafePointer<CChar>, задокументировано в разделе "Постоянные указатели" Взаимодействие с API-интерфейсами C. В нем говорится:

Строка будет автоматически преобразована в UTF8 в буфере, и указатель на этот буфер будет передан функции.

Скорее всего, это то, что вы хотите.

НО он также говорит:

Указатель, переданный функции, гарантированно действует только на время вызова функции. Не пытайтесь сохранить указатель и получить к нему доступ после возвращения функции.

Это источник вашей ошибки. sqlite3_bind_text() действительно сохраняет указатель в подготовленном документе, возможно, до тех пор, пока не будет вызван sqlite3_finalize(), чтобы он мог использовать его в будущих вызовах sqlite3_step().

Другой ответ предложил использовать NSString.UTF8String. У этого есть жизнь, которая немного длиннее и кажется, что этого должно быть достаточно. documentation говорит:

Эта строка C является указателем на структуру внутри строкового объекта, которая может иметь срок службы короче, чем строковый объект, и, вероятно, не будет иметь более продолжительный срок службы. Поэтому вы должны скопировать строку C, если она должна храниться вне контекста памяти, в котором вы используете это свойство.

"контекст памяти" здесь кажется неопределенным. Я не уверен, что это означает текущую функцию, или до тех пор, пока текущий autoreleasepool не будет исчерпан, или что-то еще. Если это один из первых двух, вы в безопасности. Если нет, я бы сказал, что вы все еще в безопасности, потому что кажется, что NSString.UTF8String долгое время использовался в таких ситуациях без проблем.