database: Add support for serializable transactions
authorTobias Brunner <tobias@strongswan.org>
Thu, 10 Oct 2013 08:58:40 +0000 (10:58 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 11 Oct 2013 13:29:10 +0000 (15:29 +0200)
src/libstrongswan/database/database.h
src/libstrongswan/plugins/mysql/mysql_database.c
src/libstrongswan/plugins/sqlite/sqlite_database.c
src/pool/pool.c

index 77d4da1..ad5ccf9 100644 (file)
@@ -115,6 +115,10 @@ struct database_t {
        /**
         * Start a transaction.
         *
+        * A serializable transaction forces a strict separation between other
+        * transactions.  Due to the performance overhead they should only be used
+        * in certain situations (e.g. SELECT->INSERT|UPDATE).
+        *
         * @note Either commit() or rollback() has to be called to end the
         * transaction.
         * @note Transactions are thread-specific. So commit()/rollbak() has to be
@@ -124,9 +128,10 @@ struct database_t {
         * not supported.  So if any if the "inner" transactions are rolled back
         * the outer most transaction is rolled back.
         *
-        * @return                      TRUE on success
+        * @param serializable  TRUE to create a serializable transaction
+        * @return                              TRUE on success
         */
-       bool (*transaction)(database_t *this);
+       bool (*transaction)(database_t *this, bool serializable);
 
        /**
         * Commit all changes made during the current transaction.
index 7a612ff..373e9dc 100644 (file)
@@ -652,7 +652,7 @@ METHOD(database_t, execute, int,
 }
 
 METHOD(database_t, transaction, bool,
-       private_mysql_database_t *this)
+       private_mysql_database_t *this, bool serializable)
 {
        transaction_t *trans = NULL;
        conn_t *conn;
@@ -669,6 +669,17 @@ METHOD(database_t, transaction, bool,
        }
        /* these statements are not supported in prepared statements that are used
         * by the execute() method */
+       if (serializable)
+       {
+               if (mysql_query(conn->mysql,
+                                               "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE") != 0)
+               {
+                       DBG1(DBG_LIB, "starting transaction failed: %s",
+                                mysql_error(conn->mysql));
+                       conn_release(this, conn);
+                       return FALSE;
+               }
+       }
        if (mysql_query(conn->mysql, "START TRANSACTION") != 0)
        {
                DBG1(DBG_LIB, "starting transaction failed: %s",
index b5ed7ee..7b47678 100644 (file)
@@ -305,9 +305,11 @@ METHOD(database_t, execute, int,
 }
 
 METHOD(database_t, transaction, bool,
-       private_sqlite_database_t *this)
+       private_sqlite_database_t *this, bool serializable)
 {
        transaction_t *trans;
+       char *cmd = serializable ? "BEGIN EXCLUSIVE TRANSACTION"
+                                                        : "BEGIN TRANSACTION";
 
        trans = this->transaction->get(this->transaction);
        if (trans)
@@ -315,7 +317,7 @@ METHOD(database_t, transaction, bool,
                ref_get(&trans->refs);
                return TRUE;
        }
-       if (execute(this, NULL, "BEGIN EXCLUSIVE TRANSACTION") == -1)
+       if (execute(this, NULL, cmd) == -1)
        {
                return FALSE;
        }
index c7cbcfc..05043cd 100644 (file)
@@ -335,7 +335,7 @@ static void add(char *name, host_t *start, host_t *end, int timeout)
        id = create_pool(name, start_addr, end_addr, timeout);
        printf("allocating %d addresses... ", count);
        fflush(stdout);
-       db->transaction(db);
+       db->transaction(db, FALSE);
        while (TRUE)
        {
                db->execute(db, NULL,
@@ -413,7 +413,7 @@ static void add_addresses(char *pool, char *path, int timeout)
        host_t *addr;
        FILE *file;
 
-       db->transaction(db);
+       db->transaction(db, FALSE);
 
        addr = host_create_from_string("%any", 0);
        pool_id = create_pool(pool, addr->get_address(addr),
@@ -559,7 +559,7 @@ static void resize(char *name, host_t *end)
        }
        DESTROY_IF(old_end);
 
-       db->transaction(db);
+       db->transaction(db, FALSE);
        if (db->execute(db, NULL,
                        "UPDATE pools SET end = ? WHERE name = ?",
                        DB_BLOB, new_addr, DB_TEXT, name) <= 0)
@@ -862,7 +862,7 @@ static void batch(char *argv0, char *name)
                exit(EXIT_FAILURE);
        }
 
-       db->transaction(db);
+       db->transaction(db, FALSE);
        while (fgets(command, sizeof(command), file))
        {
                char *argv[ARGV_SIZE], *start;