diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-12-29 05:16:51 +0000 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-12-29 06:00:08 +0000 |
commit | 99e61d33d62fa567591b37f4439297117506a18b (patch) | |
tree | 0997406eb6ef7344d0738331c6ec4a4e7e6c5443 | |
parent | Add missing override attribute (diff) | |
download | libdbpp-99e61d33d62fa567591b37f4439297117506a18b.tar.bz2 libdbpp-99e61d33d62fa567591b37f4439297117506a18b.tar.xz libdbpp-99e61d33d62fa567591b37f4439297117506a18b.zip |
Improve and centralise transaction handling logiclibdbpp-1.0.0
-rw-r--r-- | libdbpp/connection.cpp | 63 | ||||
-rw-r--r-- | libdbpp/connection.h | 23 | ||||
-rw-r--r-- | libdbpp/sqlParse.h | 1 | ||||
-rw-r--r-- | libdbpp/unittests/testConnection.cpp | 51 |
4 files changed, 122 insertions, 16 deletions
diff --git a/libdbpp/connection.cpp b/libdbpp/connection.cpp index 979c30f..33ae543 100644 --- a/libdbpp/connection.cpp +++ b/libdbpp/connection.cpp @@ -18,6 +18,11 @@ DB::TransactionStillOpen::message() const throw() return "A transaction is still open."; } +DB::Connection::Connection() : + txOpenDepth(0) +{ +} + DB::Connection::~Connection() { } @@ -42,6 +47,64 @@ DB::Connection::modify(const std::string & sql) } void +DB::Connection::finish() const +{ + if (inTx()) { + throw TransactionStillOpen(); + } +} + +void +DB::Connection::beginTx() +{ + if (inTx()) { + savepoint(stringbf("tx_sp_%p_%d", this, txOpenDepth)); + } + else { + beginTxInt(); + } + txOpenDepth += 1; +} + +void +DB::Connection::commitTx() +{ + switch (txOpenDepth) { + case 0: + throw TransactionRequired(); + case 1: + commitTxInt(); + break; + default: + releaseSavepoint(stringbf("tx_sp_%p_%d", this, txOpenDepth - 1)); + break; + } + txOpenDepth -= 1; +} + +void +DB::Connection::rollbackTx() +{ + switch (txOpenDepth) { + case 0: + throw TransactionRequired(); + case 1: + rollbackTxInt(); + break; + default: + rollbackToSavepoint(stringbf("tx_sp_%p_%d", this, txOpenDepth - 1)); + break; + } + txOpenDepth -= 1; +} + +bool +DB::Connection::inTx() const +{ + return txOpenDepth > 0; +} + +void DB::Connection::executeScript(std::istream & f, const boost::filesystem::path & s) { if (!f.good()) { diff --git a/libdbpp/connection.h b/libdbpp/connection.h index 700323b..0c03f0b 100644 --- a/libdbpp/connection.h +++ b/libdbpp/connection.h @@ -87,15 +87,15 @@ namespace DB { virtual ~Connection(); /// Perform final checks before closing. - virtual void finish() const = 0; + void finish() const; /// Open a new transaction. - virtual int beginTx() const = 0; + void beginTx(); /// Commit an open transaction. - virtual int commitTx() const = 0; + void commitTx(); /// Rollback an open transaction. - virtual int rollbackTx() const = 0; + void rollbackTx(); /// Test to see if a transaction is currently open. - virtual bool inTx() const = 0; + bool inTx() const; /// Create a named save point. virtual void savepoint(const std::string &); /// Rollback to a named save point. @@ -103,7 +103,7 @@ namespace DB { /// Release a named save point. virtual void releaseSavepoint(const std::string &); /// Test server connection availability. - virtual void ping() const = 0; + virtual void ping() const = 0; /// @cond virtual BulkDeleteStyle bulkDeleteStyle() const = 0; virtual BulkUpdateStyle bulkUpdateStyle() const = 0; @@ -143,6 +143,16 @@ namespace DB { static boost::optional<std::string> resolvePlugin(const std::type_info &, const std::string &); protected: + /// Create a new connection. + Connection(); + + /// Internal begin transaction. + virtual void beginTxInt() = 0; + /// Internal commit transaction. + virtual void commitTxInt() = 0; + /// Internal rollbacj transaction. + virtual void rollbackTxInt() = 0; + /// Internal perform table patch delete operations. virtual unsigned int patchDeletes(TablePatch * tp); /// Internal perform table patch update operations. @@ -151,6 +161,7 @@ namespace DB { virtual unsigned int patchInserts(TablePatch * tp); private: + unsigned int txOpenDepth; }; /// Helper class for beginning/committing/rolling back transactions in accordance with scope and exceptions. diff --git a/libdbpp/sqlParse.h b/libdbpp/sqlParse.h index 87b1303..4e01482 100644 --- a/libdbpp/sqlParse.h +++ b/libdbpp/sqlParse.h @@ -45,7 +45,6 @@ namespace DB { std::string statement; }; /// @endcond - } #endif diff --git a/libdbpp/unittests/testConnection.cpp b/libdbpp/unittests/testConnection.cpp index 42cecfd..d4b1610 100644 --- a/libdbpp/unittests/testConnection.cpp +++ b/libdbpp/unittests/testConnection.cpp @@ -12,14 +12,12 @@ // LCOV_EXCL_START class MockDb : public DB::Connection { public: - MockDb(const std::string &) : txDepth(0) {} - - void finish() const override {} - int beginTx() const override { return ++txDepth; } - int commitTx() const override { return --txDepth; } - int rollbackTx() const override { return --txDepth; } - bool inTx() const override { return txDepth > 0; } - void ping() const override {} + MockDb(const std::string &) {} + + void beginTxInt() override { } + void commitTxInt() override { } + void rollbackTxInt() override { } + void ping() const override {} DB::BulkDeleteStyle bulkDeleteStyle() const override { return DB::BulkDeleteUsingUsing; } DB::BulkUpdateStyle bulkUpdateStyle() const override { return DB::BulkUpdateUsingJoin; } @@ -30,7 +28,6 @@ class MockDb : public DB::Connection { DB::ModifyCommand * newModifyCommand(const std::string &) override { return nullptr; } mutable std::vector<std::string> executed; - mutable unsigned int txDepth; }; // LCOV_EXCL_STOP @@ -123,6 +120,42 @@ BOOST_AUTO_TEST_CASE( parse2 ) s.close(); } +BOOST_AUTO_TEST_CASE( finish ) +{ + auto mock = DB::ConnectionFactory::createNew("MockDb", "doesn't matter"); + BOOST_REQUIRE(mock); + BOOST_REQUIRE_EQUAL(false, mock->inTx()); + mock->beginTx(); + BOOST_REQUIRE_THROW(mock->finish(), DB::TransactionStillOpen); + mock->rollbackTx(); + mock->finish(); + mock->beginTx(); + BOOST_REQUIRE_THROW(mock->finish(), DB::TransactionStillOpen); + mock->commitTx(); + mock->finish(); + delete mock; +} + +BOOST_AUTO_TEST_CASE( tx ) +{ + auto mock = DB::ConnectionFactory::createNew("MockDb", "doesn't matter"); + BOOST_REQUIRE(mock); + BOOST_REQUIRE_EQUAL(false, mock->inTx()); + mock->beginTx(); // 1 + BOOST_REQUIRE_EQUAL(true, mock->inTx()); + mock->beginTx(); // 2 + BOOST_REQUIRE_EQUAL(true, mock->inTx()); + mock->commitTx(); // 1 + BOOST_REQUIRE_EQUAL(true, mock->inTx()); + mock->beginTx(); // 2 + BOOST_REQUIRE_EQUAL(true, mock->inTx()); + mock->rollbackTx(); // 1 + BOOST_REQUIRE_EQUAL(true, mock->inTx()); + mock->rollbackTx(); // 0 + BOOST_REQUIRE_EQUAL(false, mock->inTx()); + delete mock; +} + BOOST_AUTO_TEST_CASE( txscope ) { auto mock = DB::ConnectionFactory::createNew("MockDb", "doesn't matter"); |