diff --git a/src/SQLiteC++/SQLiteC++.h b/src/SQLiteC++/SQLiteC++.h index 57bd35a..e2053b4 100644 --- a/src/SQLiteC++/SQLiteC++.h +++ b/src/SQLiteC++/SQLiteC++.h @@ -11,3 +11,4 @@ #include "Database.h" #include "Statement.h" +#include "Transaction.h" diff --git a/src/SQLiteC++/Transaction.cpp b/src/SQLiteC++/Transaction.cpp new file mode 100644 index 0000000..bcaff90 --- /dev/null +++ b/src/SQLiteC++/Transaction.cpp @@ -0,0 +1,49 @@ +/** + * @file Transaction.cpp + * @brief A prepared SQLite Transaction is a compiled SQL query ready to be executed. + * + * Copyright (c) 2012 Sebastien Rombauts (sebastien dot rombauts at gmail dot com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#include "Transaction.h" + +#include "Database.h" + +namespace SQLite +{ + +// Compile and register the SQL query for the provided SQLite Database Connection +Transaction::Transaction(Database &aDatabase) : // throw(SQLite::Exception) + mDatabase(aDatabase), + mbCommited(false) +{ + mDatabase.exec("BEGIN"); +} + +// Finalize and unregister the SQL query from the SQLite Database Connection. +Transaction::~Transaction(void) throw() // nothrow +{ + if (false == mbCommited) + { + mDatabase.exec("ROLLBACK"); + } +} + +// Commit the transaction. +void Transaction::commit(void) // throw(SQLite::Exception) +{ + if (false == mbCommited) + { + mDatabase.exec("COMMIT"); + mbCommited = true; + } + else + { + throw SQLite::Exception("Transaction already commited"); + } +} + + +}; // namespace SQLite diff --git a/src/SQLiteC++/Transaction.h b/src/SQLiteC++/Transaction.h new file mode 100644 index 0000000..0a13586 --- /dev/null +++ b/src/SQLiteC++/Transaction.h @@ -0,0 +1,64 @@ +/** + * @file Transaction.h + * @brief A Transaction is way to group multiple SQL statements into an atomic secured operation. + * + * Copyright (c) 2012 Sebastien Rombauts (sebastien dot rombauts at gmail dot com) + * + * Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt + * or copy at http://opensource.org/licenses/MIT) + */ +#pragma once + +#include +#include "Exception.h" + +namespace SQLite +{ + +// Forward declaration +class Database; + +/** + * @brief RAII encapsulation of a SQLite Transaction. + * + * A Transaction is a way to group multiple SQL statements into an atomic secured operation; + * either it succeeds, with all the changes commited to the database file, + * or if it fails, all the changes are rolled back to the initial state. + * + * Resource Acquisition Is Initialization (RAII) means that the Transaction + * begins in the constructor and is rollbacked in the destructor, so that there is + * no need to worry about memory management or the validity of the underlying SQLite Connection. + */ +class Transaction +{ +public: + /** + * @brief Begins the SQLite transaction + * + * Exception is thrown in case of error, then the Transaction is NOT initiated. + */ + explicit Transaction(Database &aDatabase); // throw(SQLite::Exception); + + /** + * @brief Safely rollback the transaction if it has not been commited. + */ + virtual ~Transaction(void) throw(); // nothrow + + /** + * @brief Commit the transaction. + */ + void commit(void); // throw(SQLite::Exception); + +private: + // Transaction must not be copyable + Transaction(void); + Transaction(const Transaction&); + Transaction& operator=(const Transaction&); + +private: + Database& mDatabase; //!< Reference to the SQLite Database Connection + bool mbCommited; //!< True when the last executeStep() had no more row to fetch +}; + + +}; // namespace SQLite diff --git a/src/example1/main.cpp b/src/example1/main.cpp index e95e655..dda8103 100644 --- a/src/example1/main.cpp +++ b/src/example1/main.cpp @@ -15,6 +15,7 @@ #include "../SQLiteC++/Database.h" #include "../SQLiteC++/Statement.h" +#include "../SQLiteC++/Transaction.h" /// Object Oriented Basic example @@ -56,7 +57,7 @@ private: int main (void) { - // Basic example (1/3) : + // Basic example (1/4) : try { // Open a database file @@ -64,7 +65,7 @@ int main (void) std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n"; // Compile a SQL query, containing one parameter (index 1) - SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?;SELECT id FROM test"); + SQLite::Statement query(db, "SELECT * FROM test WHERE size > ?"); std::cout << "SQLite statement '" << query.getQuery().c_str() << "' compiled (" << query.getColumnCount () << " columns in the result)\n"; // Bind the integer value 6 to the first parameter of the SQL query query.bind(1, 6); @@ -99,7 +100,7 @@ int main (void) } //////////////////////////////////////////////////////////////////////////// - // Object Oriented Basic example (2/3) : + // Object Oriented Basic example (2/4) : try { // Open the database and compile the query @@ -116,7 +117,7 @@ int main (void) } //////////////////////////////////////////////////////////////////////////// - // Simple batch queries example (3/3) : + // Simple batch queries example (3/4) : try { // Open a database file @@ -138,5 +139,69 @@ int main (void) } remove("test.db3"); + //////////////////////////////////////////////////////////////////////////// + // RAII transaction example (4/4) : + try + { + // Open a database file + SQLite::Database db("transaction.db3", SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); + std::cout << "SQLite database file '" << db.getFilename().c_str() << "' opened successfully\n"; + + db.exec("DROP TABLE IF EXISTS test"); + + // Exemple of a successful transaction : + try + { + // Begin transaction + SQLite::Transaction transaction(db); + + db.exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)"); + + int nb = db.exec("INSERT INTO test VALUES (NULL, \"test\")"); + std::cout << "INSERT INTO test VALUES (NULL, \"test\")\", returned " << nb << std::endl; + + // Commit transaction + transaction.commit(); + } + catch (std::exception& e) + { + std::cout << "SQLite exception: " << e.what() << std::endl; + } + + // Exemple of a rollbacked transaction : + try + { + // Begin transaction + SQLite::Transaction transaction(db); + + int nb = db.exec("INSERT INTO test VALUES (NULL, \"second\")"); + std::cout << "INSERT INTO test VALUES (NULL, \"second\")\", returned " << nb << std::endl; + + nb = db.exec("INSERT INTO test \"error\""); + std::cout << "INSERT INTO test \"error\"\", returned " << nb << std::endl; + + // Commit transaction + transaction.commit(); + } + catch (std::exception& e) + { + std::cout << "SQLite exception: " << e.what() << std::endl; + } + + // Check the results + SQLite::Statement query(db, "SELECT * FROM test"); + std::cout << "SELECT * FROM test :\n"; + while (query.executeStep()) + { + std::cout << "row : (" << query.getColumn(0) << ", " << query.getColumn(1) << ")\n"; + } + + } + catch (std::exception& e) + { + std::cout << "SQLite exception: " << e.what() << std::endl; + } + remove("transaction.db3"); + return 0; }