diff options
Diffstat (limited to 'src/libstat/backends/sqlite3_backend.c')
-rw-r--r-- | src/libstat/backends/sqlite3_backend.c | 104 |
1 files changed, 98 insertions, 6 deletions
diff --git a/src/libstat/backends/sqlite3_backend.c b/src/libstat/backends/sqlite3_backend.c index 31b593c64..836886e1f 100644 --- a/src/libstat/backends/sqlite3_backend.c +++ b/src/libstat/backends/sqlite3_backend.c @@ -81,7 +81,8 @@ static const char *create_tables_sql = "COMMIT;"; enum rspamd_stat_sqlite3_stmt_idx { - RSPAMD_STAT_BACKEND_TRANSACTION_START = 0, + RSPAMD_STAT_BACKEND_TRANSACTION_START_IM = 0, + RSPAMD_STAT_BACKEND_TRANSACTION_START_DEF, RSPAMD_STAT_BACKEND_TRANSACTION_COMMIT, RSPAMD_STAT_BACKEND_TRANSACTION_ROLLBACK, RSPAMD_STAT_BACKEND_GET_TOKEN, @@ -102,7 +103,7 @@ static struct rspamd_sqlite3_prstmt { } prepared_stmts[RSPAMD_STAT_BACKEND_MAX] = { { - .idx = RSPAMD_STAT_BACKEND_TRANSACTION_START, + .idx = RSPAMD_STAT_BACKEND_TRANSACTION_START_IM, .sql = "BEGIN IMMEDIATE TRANSACTION;", .args = "", .stmt = NULL, @@ -110,6 +111,14 @@ static struct rspamd_sqlite3_prstmt { .ret = "" }, { + .idx = RSPAMD_STAT_BACKEND_TRANSACTION_START_DEF, + .sql = "BEGIN DEFERRED TRANSACTION;", + .args = "", + .stmt = NULL, + .result = SQLITE_DONE, + .ret = "" + }, + { .idx = RSPAMD_STAT_BACKEND_TRANSACTION_COMMIT, .sql = "COMMIT;", .args = "", @@ -313,6 +322,39 @@ rspamd_sqlite3_close_prstmt (struct rspamd_stat_sqlite3_db *db) return; } +static gboolean +rspamd_sqlite3_wait (const gchar *lock) +{ + gint fd; + struct timespec sleep_ts = { + .tv_sec = 0, + .tv_nsec = 1000000 + }; + + fd = open (lock, O_RDONLY); + + if (fd == -1) { + msg_err ("cannot open lock file %s: %s", lock, strerror (errno)); + + return FALSE; + } + + while (!rspamd_file_lock (fd, TRUE)) { + if (nanosleep (&sleep_ts, NULL) == -1 && errno != EINTR) { + close (fd); + msg_err ("cannot sleep open lock file %s: %s", lock, strerror (errno)); + + return FALSE; + } + } + + rspamd_file_unlock (fd, FALSE); + + close (fd); + + return TRUE; +} + static struct rspamd_stat_sqlite3_db * rspamd_sqlite3_opendb (const gchar *path, const ucl_object_t *opts, gboolean create, GError **err) @@ -320,15 +362,35 @@ rspamd_sqlite3_opendb (const gchar *path, const ucl_object_t *opts, struct rspamd_stat_sqlite3_db *bk; sqlite3 *sqlite; sqlite3_stmt *stmt; - gint rc, flags; - static const char sqlite_wal[] = "PRAGMA journal_mode=WAL;", - fallback_journal[] = "PRAGMA journal_mode=OFF;", + gint rc, flags, lock_fd; + gchar lock_path[PATH_MAX]; + static const char sqlite_wal[] = "PRAGMA journal_mode=\"wal\";", + fallback_journal[] = "PRAGMA journal_mode=\"off\";", user_version[] = "PRAGMA user_version;"; flags = SQLITE_OPEN_READWRITE; if (create) { flags |= SQLITE_OPEN_CREATE; + + rspamd_snprintf (lock_path, sizeof (lock_path), "%s.lock", path); + lock_fd = open (lock_path, O_WRONLY|O_CREAT|O_EXCL, 00600); + + if (lock_fd == -1 && (errno == EEXIST || errno == EBUSY)) { + if (!rspamd_sqlite3_wait (lock_path)) { + g_set_error (err, rspamd_sqlite3_quark (), + errno, "cannot create sqlite file %s: %s", + path, strerror (errno)); + + return NULL; + } + + /* At this point we have database created */ + create = FALSE; + } + else { + g_assert (rspamd_file_lock (lock_fd, FALSE)); + } } else if (access (path, R_OK) == -1) { g_set_error (err, rspamd_sqlite3_quark (), @@ -378,9 +440,16 @@ rspamd_sqlite3_opendb (const gchar *path, const ucl_object_t *opts, -1, "cannot execute create sql `%s`: %s", create_tables_sql, sqlite3_errmsg (sqlite)); sqlite3_close (sqlite); + rspamd_file_unlock (lock_fd, FALSE); + unlink (lock_path); + close (lock_fd); return NULL; } + + rspamd_file_unlock (lock_fd, FALSE); + unlink (lock_path); + close (lock_fd); } bk = g_slice_alloc0 (sizeof (*bk)); @@ -538,6 +607,11 @@ rspamd_sqlite3_process_token (struct rspamd_task *task, struct token_node_s *tok return FALSE; } + if (!bk->in_transaction) { + rspamd_sqlite3_run_prstmt (bk, RSPAMD_STAT_BACKEND_TRANSACTION_START_DEF); + bk->in_transaction = TRUE; + } + memcpy (&idx, tok->data, sizeof (idx)); /* TODO: language and user support */ @@ -559,6 +633,24 @@ rspamd_sqlite3_process_token (struct rspamd_task *task, struct token_node_s *tok return TRUE; } +void +rspamd_sqlite3_finalize_process (struct rspamd_task *task, gpointer runtime, + gpointer ctx) +{ + struct rspamd_stat_sqlite3_rt *rt = runtime; + struct rspamd_stat_sqlite3_db *bk; + + g_assert (rt != NULL); + bk = rt->db; + + if (bk->in_transaction) { + rspamd_sqlite3_run_prstmt (bk, RSPAMD_STAT_BACKEND_TRANSACTION_COMMIT); + bk->in_transaction = FALSE; + } + + return; +} + gboolean rspamd_sqlite3_learn_token (struct rspamd_task *task, struct token_node_s *tok, struct rspamd_token_result *res, gpointer p) @@ -582,7 +674,7 @@ rspamd_sqlite3_learn_token (struct rspamd_task *task, struct token_node_s *tok, } if (!bk->in_transaction) { - rspamd_sqlite3_run_prstmt (bk, RSPAMD_STAT_BACKEND_TRANSACTION_START); + rspamd_sqlite3_run_prstmt (bk, RSPAMD_STAT_BACKEND_TRANSACTION_START_IM); bk->in_transaction = TRUE; } |