LCOV - code coverage report
Current view: top level - lib - swap-table.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 123 138 89.1 %
Date: 2015-09-30 14:09:30 Functions: 11 11 100.0 %

          Line data    Source code
       1             : #include <string.h>
       2             : #include <stdlib.h>
       3             : #include <unistd.h>
       4             : #include <errno.h>
       5             : 
       6             : #include "config.h"
       7             : #include "swap-table.h"
       8             : 
       9             : #if HAVE_SQLITE3
      10             : 
      11             : #define SET_ERROR(...) g_set_error(error, RM_ERROR_QUARK, 0, __VA_ARGS__);
      12             : 
      13             : /////////////////////
      14             : //  GENERAL TYPES  //
      15             : /////////////////////
      16             : 
      17             : enum { STMT_CREATE_ATTR, STMT_INSERT_DATA, STMT_SELECT_DATA, N_STMTS };
      18             : 
      19             : static const char *STATEMENTS[] =
      20             :     {[STMT_CREATE_ATTR] = "CREATE TABLE %s_vec (%s BLOB NOT NULL);",
      21             :      [STMT_INSERT_DATA] = "INSERT INTO %s_vec VALUES(?); -- %s",
      22             :      [STMT_SELECT_DATA] = "SELECT %s FROM %s_vec WHERE rowid = ?;", [N_STMTS] = NULL};
      23             : 
      24             : typedef struct RmSwapAttr {
      25             :     int id;
      26             :     const char *name;
      27             :     sqlite3_stmt *stmts[N_STMTS];
      28             : } RmSwapAttr;
      29             : 
      30             : /////////////////////////
      31             : //  UTILITY FUNCTIONS  //
      32             : /////////////////////////
      33             : 
      34        2622 : static RmSwapAttr *rm_swap_attr_create(sqlite3 *handle, int id, const char *name,
      35             :                                        GError **error) {
      36        2622 :     g_assert(name);
      37             : 
      38        2622 :     RmSwapAttr *self = g_malloc0(sizeof(RmSwapAttr));
      39        2622 :     self->name = name;
      40        2622 :     self->id = id;
      41             : 
      42       10488 :     for(int idx = 0; idx < N_STMTS; ++idx) {
      43        7866 :         char *dynamic_sql = sqlite3_mprintf(STATEMENTS[idx], name, name);
      44             : 
      45        7866 :         if(sqlite3_prepare_v2(handle, dynamic_sql, -1, &self->stmts[idx], NULL) !=
      46             :            SQLITE_OK) {
      47           0 :             SET_ERROR("Unable to prepare statement");
      48             :         }
      49             : 
      50        7866 :         if(idx == STMT_CREATE_ATTR) {
      51        2622 :             sqlite3_stmt *stmt = self->stmts[idx];
      52        2622 :             if(sqlite3_step(stmt) != SQLITE_DONE) {
      53           0 :                 SET_ERROR("Unable to create attr table");
      54             :             }
      55             : 
      56        2622 :             if(sqlite3_reset(stmt) != SQLITE_OK) {
      57           0 :                 SET_ERROR("Unable to reset attr attr table stmt");
      58             :             }
      59             :         }
      60             : 
      61        7866 :         if(dynamic_sql != NULL) {
      62        7866 :             sqlite3_free(dynamic_sql);
      63             :         }
      64             :     }
      65             : 
      66        2622 :     return self;
      67             : }
      68             : 
      69        2622 : static void rm_swap_attr_destroy(RmSwapAttr *self, RmSwapTable *table) {
      70        2622 :     g_assert(self);
      71        2622 :     g_assert(table);
      72             : 
      73       10488 :     for(int idx = 0; idx < N_STMTS; ++idx) {
      74        7866 :         sqlite3_stmt *stmt = self->stmts[idx];
      75        7866 :         sqlite3_finalize(stmt);
      76             :     }
      77             : 
      78        2622 :     g_free(self);
      79        2622 : }
      80             : 
      81       70099 : static void rm_swap_table_clean_stmt(RmSwapTable *self, sqlite3_stmt *stmt,
      82             :                                      GError **error) {
      83       70099 :     g_assert(self);
      84       70099 :     g_assert(stmt);
      85             : 
      86       70099 :     if(sqlite3_errcode(self->cache) != SQLITE_DONE) {
      87           0 :         SET_ERROR("stmt failed: %s", sqlite3_errmsg(self->cache));
      88             :     }
      89             : 
      90       70099 :     if(sqlite3_reset(stmt) != SQLITE_OK) {
      91           0 :         SET_ERROR("Unable to reset prepared statement");
      92             :     }
      93             : 
      94       70099 :     if(sqlite3_clear_bindings(stmt) != SQLITE_OK) {
      95           0 :         SET_ERROR("Unable to clear prepared statement");
      96             :     }
      97       70099 : }
      98             : 
      99        1311 : static void rm_swap_table_create_cachedir(GError **error) {
     100        1311 :     char *path = g_build_filename(g_get_user_cache_dir(), "rmlint", NULL);
     101             : 
     102        1311 :     if(g_mkdir_with_parents(path, 0775) == -1) {
     103           0 :         SET_ERROR("cannot create cache dir %s: %s", path, g_strerror(errno));
     104             :     }
     105             : 
     106        1311 :     g_free(path);
     107        1311 : }
     108             : 
     109             : //////////////////////
     110             : //  SWAP TABLE API  //
     111             : //////////////////////
     112             : 
     113        1311 : RmSwapTable *rm_swap_table_open(gboolean in_memory, GError **error) {
     114        1311 :     RmSwapTable *self = NULL;
     115             : 
     116        1311 :     char *path = NULL;
     117             : 
     118        1311 :     if(in_memory) {
     119           0 :         path = g_strdup(":memory:");
     120             :     } else {
     121        1311 :         char pid[20] = {0};
     122        1311 :         memset(pid, 0, sizeof(pid));
     123             : 
     124        1311 :         g_snprintf(pid, sizeof(pid), "%d", getpid());
     125        1311 :         path = g_build_filename(g_get_user_cache_dir(), "rmlint", pid, NULL);
     126             : 
     127             :         /* Make sure that path actually exists */
     128        1311 :         rm_swap_table_create_cachedir(error);
     129             :     }
     130             : 
     131             :     /* Might happen if no tmp file could be created */
     132        1311 :     if(error && *error) {
     133           0 :         goto cleanup;
     134             :     }
     135             : 
     136             :     /* We do locking ourselves */
     137        1311 :     sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
     138             : 
     139        1311 :     sqlite3 *handle = NULL;
     140        1311 :     if(sqlite3_open(path, &handle) != SQLITE_OK) {
     141           0 :         SET_ERROR("Cannot open swap table db");
     142           0 :         goto cleanup;
     143             :     }
     144             : 
     145             :     /* Finetune sqlite (quite slow without these) */
     146        1311 :     sqlite3_extended_result_codes(handle, TRUE);
     147        1311 :     sqlite3_exec(handle, "PRAGMA cache_size = 8000;", 0, 0, 0);
     148        1311 :     sqlite3_exec(handle, "PRAGMA synchronous = OFF;", 0, 0, 0);
     149        1311 :     sqlite3_exec(handle, "PRAGMA journal_mode = MEMORY;", 0, 0, 0);
     150             : 
     151             : #if !RM_IS_APPLE
     152        1311 :     sqlite3_enable_shared_cache(TRUE);
     153             : #endif
     154             : 
     155        1311 :     size_t path_len = strlen(path) + 1;
     156        1311 :     self = g_malloc(sizeof(RmSwapTable) + path_len);
     157        1311 :     self->cache = handle;
     158        1311 :     self->attrs = g_ptr_array_new();
     159        1311 :     self->transaction_running = FALSE;
     160        1311 :     strncpy(self->path, path, path_len);
     161        1311 :     g_mutex_init(&self->mtx);
     162             : 
     163             : cleanup:
     164        1311 :     g_free(path);
     165        1311 :     return self;
     166             : }
     167             : 
     168        1311 : void rm_swap_table_close(RmSwapTable *self, GError **error) {
     169        1311 :     g_assert(self);
     170             : 
     171        1311 :     g_mutex_lock(&self->mtx);
     172             :     {
     173        1311 :         g_ptr_array_foreach(self->attrs, (GFunc)rm_swap_attr_destroy, self);
     174        1311 :         g_ptr_array_free(self->attrs, TRUE);
     175             : 
     176        1311 :         if(sqlite3_close(self->cache) != SQLITE_OK) {
     177           0 :             SET_ERROR("Unable to close swap table db");
     178             :         }
     179             : 
     180        1311 :         if(unlink(self->path) == -1) {
     181           0 :             SET_ERROR("cannot delete temp cache %s: %s", self->path, g_strerror(errno));
     182             :         }
     183             :     }
     184        1311 :     g_mutex_unlock(&self->mtx);
     185        1311 :     g_mutex_clear(&self->mtx);
     186        1311 :     self->cache = NULL;
     187        1311 :     self->attrs = NULL;
     188        1311 :     g_free(self);
     189        1311 : }
     190             : 
     191        2622 : int rm_swap_table_create_attr(RmSwapTable *self, const char *name, GError **error) {
     192        2622 :     g_assert(self);
     193             : 
     194        2622 :     RmSwapAttr *attribute = NULL;
     195        2622 :     g_mutex_lock(&self->mtx);
     196             :     {
     197        2622 :         attribute = rm_swap_attr_create(self->cache, self->attrs->len, name, error);
     198        2622 :         g_ptr_array_add(self->attrs, attribute);
     199             :     }
     200        2622 :     g_mutex_unlock(&self->mtx);
     201             : 
     202             :     /* Create RmSwapAttr, return id, add it to table, create table. */
     203        2622 :     return (attribute) ? attribute->id : 0;
     204             : }
     205             : 
     206        4944 : static void rm_swap_table_begin(RmSwapTable *self) {
     207        4944 :     g_assert(self);
     208             : 
     209        4944 :     if(sqlite3_exec(self->cache, "BEGIN IMMEDIATE;", 0, 0, 0) == SQLITE_OK) {
     210        4944 :         self->transaction_running = TRUE;
     211             :     }
     212        4944 : }
     213             : 
     214        4944 : static void rm_swap_table_commit(RmSwapTable *self) {
     215        4944 :     g_assert(self);
     216             : 
     217        4944 :     if(sqlite3_exec(self->cache, "COMMIT;", 0, 0, 0) == SQLITE_OK) {
     218        4944 :         self->transaction_running = FALSE;
     219             :     }
     220        4944 : }
     221             : 
     222       58413 : size_t rm_swap_table_lookup(RmSwapTable *self, int attr, RmOff id, char *buf,
     223             :                             size_t buf_size) {
     224       58413 :     g_assert(self);
     225             : 
     226       58413 :     size_t bytes_written = 0;
     227             : 
     228       58413 :     g_mutex_lock(&self->mtx);
     229             :     {
     230             :         /* Commit any stalled transactions */
     231       58417 :         if(self->transaction_running) {
     232        4944 :             rm_swap_table_commit(self);
     233             :         }
     234             : 
     235       58417 :         RmSwapAttr *attribute = self->attrs->pdata[attr];
     236       58417 :         sqlite3_stmt *stmt = attribute->stmts[STMT_SELECT_DATA];
     237             : 
     238       58417 :         if(sqlite3_bind_int64(stmt, 1, id) != SQLITE_OK) {
     239           0 :             g_assert_not_reached();
     240             :         }
     241             : 
     242       58417 :         if(sqlite3_step(stmt) == SQLITE_ROW) {
     243       58417 :             bytes_written = MIN((size_t)sqlite3_column_bytes(stmt, 0), buf_size);
     244       58417 :             memcpy(buf, sqlite3_column_blob(stmt, 0), bytes_written);
     245             :         }
     246             : 
     247       58417 :         sqlite3_step(stmt); /* == SQLITE_DONE */
     248             : 
     249       58417 :         rm_swap_table_clean_stmt(self, stmt, NULL);
     250             :     }
     251       58417 :     g_mutex_unlock(&self->mtx);
     252             : 
     253       58417 :     return bytes_written;
     254             : }
     255             : 
     256       11682 : RmOff rm_swap_table_insert(RmSwapTable *self, int attr, char *data, size_t data_len) {
     257       11682 :     g_assert(self);
     258             : 
     259       11682 :     RmOff id = 0;
     260             : 
     261       11682 :     g_mutex_lock(&self->mtx);
     262             :     {
     263             :         /* Begin an transaction if none is running yet */
     264       11682 :         if(self->transaction_running == FALSE) {
     265        4944 :             rm_swap_table_begin(self);
     266             :         }
     267             : 
     268       11682 :         RmSwapAttr *attribute = self->attrs->pdata[attr];
     269       11682 :         sqlite3_stmt *stmt = attribute->stmts[STMT_INSERT_DATA];
     270             : 
     271       11682 :         if(sqlite3_bind_text(stmt, 1, data, data_len, NULL) != SQLITE_OK) {
     272           0 :             g_assert_not_reached();
     273             :         }
     274             : 
     275       11682 :         if(sqlite3_step(stmt) == SQLITE_DONE) {
     276       11682 :             id = sqlite3_last_insert_rowid(self->cache);
     277             :         }
     278             : 
     279       11682 :         rm_swap_table_clean_stmt(self, stmt, NULL);
     280             :     }
     281       11682 :     g_mutex_unlock(&self->mtx);
     282             : 
     283       11682 :     return id;
     284             : }
     285             : 
     286             : #else /* HAVE_SQLITE3 */
     287             : 
     288             : RmSwapTable *rm_swap_table_open(_U gboolean in_memory, _U GError **error) {
     289             :     return NULL;
     290             : }
     291             : 
     292             : void rm_swap_table_close(_U RmSwapTable *self, _U GError **error) {
     293             :     return;
     294             : }
     295             : 
     296             : int rm_swap_table_create_attr(_U RmSwapTable *self, _U const char *name,
     297             :                               _U GError **error) {
     298             :     return 0;
     299             : }
     300             : 
     301             : RmOff rm_swap_table_insert(_U RmSwapTable *self, _U int attr, _U char *data,
     302             :                            _U size_t data_len) {
     303             :     return 0;
     304             : }
     305             : 
     306             : size_t rm_swap_table_lookup(_U RmSwapTable *self, _U int attr, _U RmOff id, _U char *buf,
     307             :                             _U size_t buf_size) {
     308             :     return 0;
     309             : }
     310             : 
     311             : #endif
     312             : 
     313             : //////////////////////
     314             : //  UGLY TEST MAIN  //
     315             : //////////////////////
     316             : 
     317             : // #define _RM_COMPILE_SWAP_TABLE_MAIN
     318             : #ifdef _RM_COMPILE_SWAP_TABLE_MAIN
     319             : 
     320             : int main(void) {
     321             :     GError *error = NULL;
     322             :     RmSwapTable *table = rm_swap_table_open(FALSE, &error);
     323             : 
     324             :     if(error != NULL) {
     325             :         return EXIT_FAILURE;
     326             :     }
     327             : 
     328             :     int PATH_ATTR = rm_swap_table_create_attr(table, "path", &error);
     329             : 
     330             :     const int N = 1000000;
     331             :     const int PATH_LEN = 80; /* Typical average path len */
     332             : 
     333             :     for(int i = 0; i < (N); i++) {
     334             :         char buf[PATH_LEN + 1];
     335             :         memset(buf, (i % ('~' - '!')) + '!', PATH_LEN);
     336             : 
     337             :         buf[PATH_LEN] = 0;
     338             : 
     339             :         rm_swap_table_insert(table, PATH_ATTR, buf, sizeof(buf));
     340             :     }
     341             : 
     342             :     for(int i = 0; i < (N); i++) {
     343             :         char buf[PATH_MAX];
     344             :         rm_swap_table_lookup(table, PATH_ATTR, i + 1, buf, sizeof(buf));
     345             :     }
     346             : 
     347             :     rm_swap_table_close(table, &error);
     348             : 
     349             :     if(error != NULL) {
     350             :         g_error_free(error);
     351             :     }
     352             : 
     353             :     return EXIT_SUCCESS;
     354             : }
     355             : 
     356             : #endif

Generated by: LCOV version 1.11