summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdbpp/connection.cpp63
-rw-r--r--libdbpp/connection.h23
-rw-r--r--libdbpp/sqlParse.h1
-rw-r--r--libdbpp/unittests/testConnection.cpp51
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");