Как вы привязываете параметры к инструкции SQL в SQLite3 для C ?

#c #sql #c #sqlite

#c #sql #c #sqlite

Вопрос:

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

Я пытался прочитать документацию, но я все еще относительно новичок в C , поэтому я не думаю, что полностью понимаю нюансы того, как именно они хотят, чтобы оператор переменной SQL обрабатывался. Я пробовал два разных способа, отличных от того, что я нашел путем поиска, и, похоже, я всегда получаю ошибку SQL = (null).

Первое, что я попробовал, было это: (обратите внимание, что БД открывается в другой функции, и она отлично работает для другого выполнения статического оператора SQL, который у меня был ранее.)

 void Access_Database::insertAirportJobs(float pay, int expire, float weight, Airport ap, Airport source)
{
     char* zErrMsg = 0; // Error message var
     char* sql;
     const char* pArg = "Row Tested";
     snprintf(sql, sizeof(sql), "INSERT INTO JOBS (airport_ID,dest,pay,expire,weight) " 
     "VALUES (%i, %i, %f, %i, %f ); ", source.airport_ID, ap.airport_ID, pay, expire, weight);
     sql[sizeof(sql) - 1] = '';

     int rc = sqlite3_exec(db, sql, jobsCallback, (void*)pArg, amp;zErrMsg); // Execute the SQL statement that was passed in.

     if (rc != SQLITE_OK)
     {
         fprintf(stderr, "nSQL error: %snn", zErrMsg);
         sqlite3_free(zErrMsg);
     }
     else
     {
         fprintf(stdout, "Operation done successfullyn");
     }
}
  

Это не сработало и выдало сообщение об ошибке, которое я отмечал ранее, поэтому я вернулся к поиску в Google и нашел это решение:

 void Access_Database::insertAirportJobs(float pay, int expire, float weight, Airport ap, Airport source)
{
    char* zErrMsg = 0; // Error message var
    const char* sql = "INSERT INTO JOBS (airport_ID,dest,pay,expire,weight) VALUES (?, ?, ?, ?, ?)";
    sqlite3_stmt* stmt;
    const char* pszTest;
    int rc = sqlite3_prepare_v2(db, sql, strlen(sql), amp;stmt, amp;pszTest);
    if (rc == SQLITE_OK)
    {
        sqlite3_bind_int(stmt, 1, source.airport_ID);
        sqlite3_bind_int(stmt, 2, ap.airport_ID);
        sqlite3_bind_double(stmt, 3, pay);
        sqlite3_bind_int(stmt, 4, expire);
        sqlite3_bind_double(stmt, 5, weight);
        
        // Commit the binds

        sqlite3_step(stmt);
        sqlite3_finalize(stmt);

    }
    
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "nSQL error: %snn", zErrMsg);
        sqlite3_free(zErrMsg);
    }
    else
    {
        fprintf(stdout, "Operation done successfullyn");
    }
}
  

Это тоже не сработало и выдало ту же ошибку. Если вместо этого я помещаю код, который отображается, когда я использую точку останова для проверки того, что происходит на сервере, в графический интерфейс, он работает отлично. Я действительно не знаю, что происходит не так, потому что сообщение об ошибке на самом деле не помогло мне найти что-нибудь хорошее в Google. Возможно, кто-нибудь из вас может просто рассказать мне о правильном методе для динамического оператора SQL для SQLite3. Любая помощь будет с благодарностью.

РЕДАКТИРОВАТЬ: по-видимому, я закрывал базу данных ранее в функции, которую я использовал для вызова этой функции. Упс. Второй пример в основном работал с некоторыми незначительными изменениями.

Комментарии:

1. первое решение не работает, snprintf не выделяет никакой памяти. Вам необходимо предоставить функции область памяти (пример: char SqlRequest[100] или динамически выделяемая память с помощью malloc)

2. во втором решении на первый взгляд все в порядке, но в логике что-то не так. Смысл инструкции в том, чтобы сохранить ее и повторно использовать несколько раз, в вашем примере вы просто используете ее один раз. Вы можете сохранить его в своем классе и инициализировать в начале, уничтожить его в конце.

3. Если вам нужна дополнительная информация об ошибке, вы можете распечатать ошибку с помощью sqlite3_errmsg(Database)

4. @Robert Для первого примера я попытался сначала инициализировать его как NULL, а затем вместо этого установить его размер, подобный sizeof(* sql), потому что я думал, что это то, что вы имели в виду, хотя я все еще получал сообщение об ошибке, в котором говорилось о неправильном параметре или другом неправильном использовании API. Для второго я просто исправил принтер сообщений об ошибках, и он напечатал ту же ошибку, что и первый. Смысл кода заключается в его повторном использовании. Я действительно не знаю, что вы подразумеваете под этим вторым комментарием, поскольку эта функция вызывается каждый раз, когда в программе создается новое «задание». Есть идеи?

5. ИСПРАВЛЕНО. Очевидно, я ранее закрыл свою базу данных в функции, и именно поэтому она не смогла получить к ней доступ. Упс. @Robert

Ответ №1:

В вашей первой версии sql неинициализирован, даже если это не так sizeof(sql) , возвращает размер указателя, а не размер массива.

В вашей второй версии ваше сообщение об ошибке равно нулю, потому что вы никогда не присваиваете ему значение, вы должны вызывать sqlite3_errmsg , вы не должны вызывать sqlite3_free(zErrMsg); , поскольку sqlite управляет собственной памятью для сообщений об ошибках.

Комментарии:

1. На самом деле, если sqlite3_exec() устанавливается сообщение об ошибке, оно должно быть освобождено, sqlite3_free() когда вы закончите с ним, как сказано в документации . Конечно, не для второго блока кода OP.

2. @Shawn Хорошо, да, я говорил о второй версии

3. Для первого примера я попытался сначала инициализировать его как NULL, а затем вместо этого установить его размер like sizeof(*sql) , потому что я думал, что это то, что вы имели в виду, хотя я все еще получал сообщение об ошибке, в котором говорилось о неправильном параметре или другом неправильном использовании API. Для второго я просто исправил принтер сообщений об ошибках, и он напечатал ту же ошибку, что и первый. Есть идеи @AlanBirtles?

4. ИСПРАВЛЕНО. Очевидно, я ранее закрыл свою базу данных в функции, и именно поэтому она не смогла получить к ней доступ. Упс. @AlanBirtles