Sqlite3_prepare_v2/sqlite3_exec

Несколько вопросов о sqlite3:

1.Когда необходимо использовать первый подход один, а другой? Это разница между ними?

sqlite3_prepare_v2(_contactDB, sql_stmt_getIdRecepteur, -1, &sqlStatement, NULL);

и

if(sqlite3_prepare_v2(_contactDB, sql_stmt_getIdRecepteur, -1, &sqlStatement, NULL) == SQLITE_OK) {}

2.Как чаще всего указывается использование "sqlite3_exec", чем "sqlite3_prepare_v2"?

3. Когда необходимо использовать первый, второй или третий:

while(sqlite3_step(sqlStatement) == SQLITE_ROW){}
if(sqlite3_step(sqlStatement) == SQLITE_ROW){}
if(sqlite3_step(sqlStatement) == SQLITE_DONE){}

Заранее благодарю

Ответ 1

  1. Нужно всегда проверять возвращаемые значения функций SQLite, чтобы убедиться, что они выполнены успешно, поэтому использование оператора if является наиболее предпочтительным. И если это не удалось, можно было бы вызвать sqlite3_errmsg() чтобы получить описание ошибки в строке C.

  2. Можно использовать sqlite3_prepare_v2 (вместо sqlite3_exec) в любой ситуации, в которой:

    • один возвращает данные и поэтому вызывает sqlite3_step за которым следует одна или несколько функций sqlite3_column_xxx, повторяя этот процесс для каждой строки данных; или же

    • один привязывает значения к ? заполнители в SQL с sqlite3_bind_xxx.

    Из вышесказанного можно сделать вывод, что использовать sqlite3_exec только в том случае, если (а) строка SQL не имеет параметров; и (б) SQL не возвращает никаких данных. sqlite3_exec проще, но его следует использовать только в этих конкретных ситуациях.

    Пожалуйста, обратите внимание: что касается ? Заполнители очень важны: следует избегать создания операторов SQL вручную (например, с помощью stringWithFormat или Swift с интерполяцией строк), особенно если вводимые значения включают ввод для конечного пользователя. Например, если вы вызываете sqlite3_exec с оператором INSERT, UPDATE или DELETE который был создан с использованием пользовательского ввода (например, вставка некоторого значения, предоставленного пользователем, в базу данных), у вас есть реальная возможность возникновения проблем, связанных с неэкранированными кавычками и escape-символы и т.д. Один также подвергается атакам SQL-инъекций.

    Например, если commentString был предоставлен в результате ввода данных пользователем, это было бы нежелательно:

    NSString *sql = [NSString stringWithFormat:@"INSERT INTO COMMENTS (COMMENT) VALUES ('%@')", commentString];
    if (sqlite3_exec(database, [sql UTF8String], NULL, NULL, NULL) != SQLITE_OK) {
        NSLog(@"Insert failure: %s", sqlite3_errmsg(database));
    }
    

    Вместо этого вы должны:

    const char *sql = "INSERT INTO COMMENTS (COMMENT) VALUES (?)";
    if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK) {
        NSLog(@"Prepare failure: %s", sqlite3_errmsg(database));
        return;
    }
    if (sqlite3_bind_text(statement, 1, [commentString UTF8String], -1, SQLITE_TRANSIENT) != SQLITE_OK) {
        NSLog(@"Bind 1 failure: %s", sqlite3_errmsg(database));
        sqlite3_finalize(statement);
        return;
    }
    if (sqlite3_step(statement) != SQLITE_DONE) {
        NSLog(@"Step failure: %s", sqlite3_errmsg(database));
    }
    sqlite3_finalize(statement);
    

    Обратите внимание, что если эта правильная реализация кажется слишком сложной, вы можете использовать библиотеку FMDB, что упростит ее до:

    if (![db executeUpdate:@"INSERT INTO COMMENTS (COMMENT) VALUES (?)", commentString]) {
        NSLog(@"Insert failure: %@", [db lastErrorMessage]);
    }
    

    Это обеспечивает строгость подхода sqlite3_prepare_v2, но простоту интерфейса sqlite3_exec.

  3. При извлечении нескольких строк данных можно использовать:

    while(sqlite3_step(sqlStatement) == SQLITE_ROW) { ... }
    

    Или, лучше, если вы хотите сделать правильную обработку ошибок, вы должны сделать:

    int rc;
    while ((rc = sqlite3_step(sqlStatement)) == SQLITE_ROW) {
        // process row here
    }
    if (rc != SQLITE_DONE) {
         NSLog(@"Step failure: %s", sqlite3_errmsg(database));
    }
    

    При извлечении одной строки данных можно:

    if (sqlite3_step(sqlStatement) != SQLITE_ROW) {
        NSLog(@"Step failure: %s", sqlite3_errmsg(database));
    }
    

    При выполнении SQL, который не будет возвращать никаких данных, можно:

    if (sqlite3_step(sqlStatement) != SQLITE_DONE) {
        NSLog(@"Step failure: %s", sqlite3_errmsg(database));
    }
    

При использовании интерфейса SQLite C вы можете видеть, что для правильной работы требуется небольшая работа. Вокруг этого интерфейса есть тонкая оболочка Objective-C под названием FMDB, которая не только упрощает взаимодействие с базой данных SQLite, но и немного более надежна.

Ответ 2

Для вопроса 1, в большинстве случаев вам нужно убедиться, что результат равен SQLITE_OK, чтобы убедиться, что ваша команда успешно запущена. ( SQLITE_OK - int type **). Следовательно, предпочтительна вторая.

Для вопроса 2 функция sqlite3_exec используется для запуска любой команды, которая не возвращает данные, включая обновления, вставки и удаления. Извлечение данных из базы данных немного более активно. И функция sqlite3_prepare_v2 может использоваться для SELECTSQL). В общем случае создание таблицы часто использует первый.

Для вопроса 3, ну, , а для цикла, а , если для условия. Как правило, если вы извлекаете dada из db, вам нужен цикл для перемещения * возвращаемого массива **. Если вы вставляете данные в db (например), вы можете использовать SQLITE_DONE для проверки работы.

Кстати, основные данные предпочтительнее в IOS для большинства случаев.

Ответ 3

Поздний ответ на №2, который я только что нашел: используйте sqlite3_exec (или используйте sqlite3_prepare_v2 в цикле), когда sql_stmt_getIdRecepteur фактически содержит несколько операторов SQL. Из docs для sqlite3_prepare_v2:

Эти процедуры только компилируют первый оператор в zSql, поэтому * pzTail остается указывать на то, что остается нескомпилированным.

sqlite3_exec включает внутренний цикл, который вызывает sqlite3_prepare_v2 несколько раз, пока не будет скомпилирована вся входная строка. Если вы не используете sqlite3_exec, и у вас есть несколько операторов SQL в строке, вам нужно проверить возвращаемое значение pzTail от sqlite3_prepare_v2.

Ответ 4

Вот простой, полный пример C, который иллюстрирует концепции подготовки/шага:

https://gist.github.com/jsok/2936764

Это может быть использовано для эмуляции курсора.