From 39ebf65fa5605de25cb54460119c88ce0402db85 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 10 Jul 2015 14:13:39 +0100 Subject: [PATCH] Unify sqlite open and create function. --- src/libstat/backends/sqlite3_backend.c | 142 ++--------------------- src/libutil/sqlite_utils.c | 152 +++++++++++++++++++++++++ src/libutil/sqlite_utils.h | 9 ++ 3 files changed, 168 insertions(+), 135 deletions(-) diff --git a/src/libstat/backends/sqlite3_backend.c b/src/libstat/backends/sqlite3_backend.c index 94252ceb2..4ded6198d 100644 --- a/src/libstat/backends/sqlite3_backend.c +++ b/src/libstat/backends/sqlite3_backend.c @@ -181,150 +181,28 @@ rspamd_sqlite3_backend_quark (void) return g_quark_from_static_string ("sqlite3-stat-backend"); } -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) { struct rspamd_stat_sqlite3_db *bk; - sqlite3 *sqlite; - sqlite3_stmt *stmt; - 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; -#ifdef SQLITE_OPEN_SHAREDCACHE - flags |= SQLITE_OPEN_SHAREDCACHE; -#endif -#ifdef SQLITE_OPEN_WAL - flags |= SQLITE_OPEN_WAL; -#endif - - 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_backend_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_backend_quark (), - errno, "cannot open sqlite file %s: %s", - path, strerror (errno)); - return NULL; - } + bk = g_slice_alloc0 (sizeof (*bk)); + bk->sqlite = rspamd_sqlite3_open_or_create (path, create_tables_sql, err); - if ((rc = sqlite3_open_v2 (path, &sqlite, - flags, NULL)) != SQLITE_OK) { -#if SQLITE_VERSION_NUMBER >= 3008000 - g_set_error (err, rspamd_sqlite3_backend_quark (), - rc, "cannot open sqlite db %s: %s", - path, sqlite3_errstr (rc)); -#else - g_set_error (err, rspamd_sqlite3_backend_quark (), - rc, "cannot open sqlite db %s: %d", - path, rc); -#endif + if (bk->sqlite == NULL) { + g_slice_free1 (sizeof (*bk), bk); return NULL; } - if (sqlite3_exec (sqlite, sqlite_wal, NULL, NULL, NULL) != SQLITE_OK) { - msg_warn ("WAL mode is not supported, locking issues might occur"); - sqlite3_exec (sqlite, fallback_journal, NULL, NULL, NULL); - } - - /* Check user_version */ - if (!create) { - g_assert (sqlite3_prepare_v2 (sqlite, user_version, -1, &stmt, NULL) - == SQLITE_OK); - g_assert (sqlite3_step (stmt) == SQLITE_ROW); - - if (sqlite3_column_int (stmt, 0) != atoi (SQLITE3_SCHEMA_VERSION)) { - msg_warn ("bad sqlite database: %s, try to recreate it", path); - create = TRUE; - } - - sqlite3_finalize (stmt); - } - - if (create) { - if (sqlite3_exec (sqlite, create_tables_sql, NULL, NULL, NULL) != SQLITE_OK) { - g_set_error (err, rspamd_sqlite3_backend_quark (), - -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)); - bk->sqlite = sqlite; - bk->prstmt = rspamd_sqlite3_init_prstmt (sqlite, prepared_stmts, + bk->prstmt = rspamd_sqlite3_init_prstmt (bk->sqlite, prepared_stmts, RSPAMD_STAT_BACKEND_MAX, err); if (bk->prstmt == NULL) { + sqlite3_close (bk->sqlite); g_slice_free1 (sizeof (*bk), bk); - sqlite3_close (sqlite); return NULL; } @@ -374,21 +252,15 @@ rspamd_sqlite3_init (struct rspamd_stat_ctx *ctx, filename = ucl_object_tostring (filenameo); - if ((bk = rspamd_sqlite3_opendb (filename, stf->opts, FALSE, + if ((bk = rspamd_sqlite3_opendb (filename, stf->opts, TRUE, &err)) == NULL) { msg_err ("cannot open sqlite3 db: %e", err); - g_error_free (err); - err = NULL; - - bk = rspamd_sqlite3_opendb (filename, stf->opts, TRUE, - &err); } if (bk != NULL) { g_hash_table_insert (new->files, stf, bk); } else { - msg_err ("cannot create sqlite3 db: %e", err); g_error_free (err); err = NULL; } diff --git a/src/libutil/sqlite_utils.c b/src/libutil/sqlite_utils.c index ba91a874f..c07223c6e 100644 --- a/src/libutil/sqlite_utils.c +++ b/src/libutil/sqlite_utils.c @@ -172,3 +172,155 @@ rspamd_sqlite3_close_prstmt (sqlite3 *db, GArray *stmts) 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; +} + + + +sqlite3 * +rspamd_sqlite3_open_or_create (const gchar *path, const + gchar *create_sql, GError **err) +{ + sqlite3 *sqlite; + sqlite3_stmt *stmt; + gint rc, flags, lock_fd; + gchar lock_path[PATH_MAX], dbdir[PATH_MAX], *pdir; + static const char sqlite_wal[] = "PRAGMA journal_mode=\"wal\";", + exclusive_lock_sql[] = "PRAGMA locking_mode=\"exclusive\";"; + gboolean create = FALSE; + + flags = SQLITE_OPEN_READWRITE; +#ifdef SQLITE_OPEN_SHAREDCACHE + flags |= SQLITE_OPEN_SHAREDCACHE; +#endif +#ifdef SQLITE_OPEN_WAL + flags |= SQLITE_OPEN_WAL; +#endif + + rspamd_strlcpy (dbdir, path, sizeof (dbdir)); + pdir = dirname (dbdir); + + if (access (pdir, W_OK) == -1) { + g_set_error (err, rspamd_sqlite3_quark (), + errno, "cannot open sqlite directory %s: %s", + pdir, strerror (errno)); + + return NULL; + } + + if (access (path, R_OK) == -1) { + 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)); + create = TRUE; + } + } + + if ((rc = sqlite3_open_v2 (path, &sqlite, + flags, NULL)) != SQLITE_OK) { +#if SQLITE_VERSION_NUMBER >= 3008000 + g_set_error (err, rspamd_sqlite3_quark (), + rc, "cannot open sqlite db %s: %s", + path, sqlite3_errstr (rc)); +#else + g_set_error (err, rspamd_sqlite3_quark (), + rc, "cannot open sqlite db %s: %d", + path, rc); +#endif + + return NULL; + } + + if (create) { + if (sqlite3_exec (sqlite, exclusive_lock_sql, NULL, NULL, NULL) != SQLITE_OK) { + msg_warn ("cannot exclusively lock database to create schema"); + } + + if (sqlite3_exec (sqlite, create_sql, NULL, NULL, NULL) != SQLITE_OK) { + g_set_error (err, rspamd_sqlite3_quark (), + -1, "cannot execute create sql `%s`: %s", + create_sql, sqlite3_errmsg (sqlite)); + sqlite3_close (sqlite); + rspamd_file_unlock (lock_fd, FALSE); + unlink (lock_path); + close (lock_fd); + + return NULL; + } + + sqlite3_close (sqlite); + rspamd_file_unlock (lock_fd, FALSE); + unlink (lock_path); + close (lock_fd); + + /* Reopen in normal mode */ + flags &= ~SQLITE_OPEN_CREATE; + if ((rc = sqlite3_open_v2 (path, &sqlite, + flags, NULL)) != SQLITE_OK) { + #if SQLITE_VERSION_NUMBER >= 3008000 + g_set_error (err, rspamd_sqlite3_quark (), + rc, "cannot open sqlite db after creation %s: %s", + path, sqlite3_errstr (rc)); + #else + g_set_error (err, rspamd_sqlite3_quark (), + rc, "cannot open sqlite db after creation %s: %d", + path, rc); + #endif + + return NULL; + } + } + + if (sqlite3_exec (sqlite, sqlite_wal, NULL, NULL, NULL) != SQLITE_OK) { + msg_warn ("WAL mode is not supported, locking issues might occur"); + } + + return sqlite; +} diff --git a/src/libutil/sqlite_utils.h b/src/libutil/sqlite_utils.h index 5cb71f3ad..18dd20a6f 100644 --- a/src/libutil/sqlite_utils.h +++ b/src/libutil/sqlite_utils.h @@ -68,4 +68,13 @@ gint rspamd_sqlite3_run_prstmt (sqlite3 *db, GArray *stmts, */ void rspamd_sqlite3_close_prstmt (sqlite3 *db, GArray *stmts); +/** + * Creates or opens sqlite database trying to share it between processes + * @param path + * @param create_sql + * @return + */ +sqlite3 * rspamd_sqlite3_open_or_create (const gchar *path, const + gchar *create_sql, GError **err); + #endif /* SRC_LIBUTIL_SQLITE_UTILS_H_ */ -- 2.39.5