Предполагается, что для подготовки требуется эксклюзивный доступ для чтения данных текущей схемы базы данных.
Одним из возможных решений будет использование sqlite3_busy_handler, например,
#define ABORT 0
#define CONTINUE 1
int busy_handler(void *data, int attempt) {
printf("attempt: %d\n", attempt);
if(attempt < 10) {
sqlite3_sleep(250);
return CONTINUE;
}
return ABORT;
}
и установка его перед вызов sqlite3_prepare_v2
примерно так:
sqlite3_busy_handler(db, busy_handler, NULL);
Максимальное количество попыток и время ожидания (в миллисекундах) должны определяться на основе ваших требований.
Как уже упоминалось в Комментарии имеет смысл проверить код возврата вызова sqlite3_open_v2.
Предположительно, было бы также целесообразно использовать подготовленное утверждение следующим образом:
rc = sqlite3_prepare_v2(db, "SELECT value FROM dataplc WHERE address = ?1", -1, &stmt, NULL);
и связать параметр с помощью:
sqlite3_bind_int(stmt, 1, addr);
Как проверить
Для тестирования можно использовать трюк, который можно найти в этом прекрасном ответе: { ссылка }:
Можно просто установить время сна, например временно до 1 секунды (в busy_handler sqlite3_sleep(1000);
).
Затем добавьте getchar();
непосредственно перед подготовкой.
Ваша программа, слегка измененная в отношении вышеупомянутых пунктов, будет выглядеть так:
#include <stdio.h>
#include <sys/syslog.h>
#include "sqlite3.h"
#define ABORT 0
#define CONTINUE 1
int busy_handler(void *data, int attempt) {
printf("attempt: %d\n", attempt);
if (attempt < 10) {
sqlite3_sleep(1000);
return CONTINUE;
}
return ABORT;
}
int read_from_db(int addr) {
sqlite3 *db;
sqlite3_stmt *stmt;
int rc, value = 0;
rc = sqlite3_open_v2("mydb", &db, SQLITE_OPEN_READONLY, NULL);
if (rc != SQLITE_OK) {
sqlite3_close(db);
syslog(LOG_INFO, "Failed to open db\n");
return -1;
}
sqlite3_busy_handler(db, busy_handler, NULL);
printf("press enter to continue:\n");
getchar(); //only for testing
rc = sqlite3_prepare_v2(db, "SELECT value FROM dataplc WHERE address = ?1", -1, &stmt, NULL);
if (rc != SQLITE_OK) {
syslog(LOG_INFO, "Failed to read: %d - rc: %d", addr, rc); // -5 -> db busy
value = -1;
} else {
sqlite3_bind_int(stmt, 1, addr);
rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
value = sqlite3_column_int(stmt, 0);
// do something
}
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return value;
}
int main(void) {
int val = read_from_db(42);
printf("result: %d\n", val);
return 0;
}
В командной строке sqlite3 В интерфейс можно ввести:
begin exclusive;
Затем на консоли, где запускается программа, вы можете нажать клавишу ENTER. Теперь он покажет printfs из busy_handler. Как только мы освободим базу данных с
commit;
, результат будет возвращен.