From bbfca1594e950f400b3c4d5ab7c83cbc6549b051 Mon Sep 17 00:00:00 2001 From: silverqx Date: Fri, 14 Jan 2022 21:26:02 +0100 Subject: [PATCH] extracted transactions to ManagesTransactions Transactions extracted to the Concerns::ManagesTransactions base class. - bugfix includes - logConnected()/logDisconnected() code wrapped in TINYORM_MYSQL_PING - hitTransactionalCounters() extracted to Concerns::CountsQueries - convertNamedToPositionalBindings() extracted to Concerns::LogsQueries --- cmake/Modules/TinySources.cmake | 2 + include/include.pri | 1 + include/orm/concerns/countsqueries.hpp | 11 +- include/orm/concerns/logsqueries.hpp | 7 + include/orm/concerns/managestransactions.hpp | 109 ++++++++ include/orm/databaseconnection.hpp | 78 ++---- src/orm/concerns/countsqueries.cpp | 25 ++ src/orm/concerns/logsqueries.cpp | 16 ++ src/orm/concerns/managestransactions.cpp | 248 +++++++++++++++++++ src/orm/databaseconnection.cpp | 245 +----------------- src/src.pri | 1 + 11 files changed, 442 insertions(+), 301 deletions(-) create mode 100644 include/orm/concerns/managestransactions.hpp create mode 100644 src/orm/concerns/managestransactions.cpp diff --git a/cmake/Modules/TinySources.cmake b/cmake/Modules/TinySources.cmake index a946ffe11..f21ca570c 100644 --- a/cmake/Modules/TinySources.cmake +++ b/cmake/Modules/TinySources.cmake @@ -16,6 +16,7 @@ function(tiny_sources out_headers out_sources) concerns/detectslostconnections.hpp concerns/hasconnectionresolver.hpp concerns/logsqueries.hpp + concerns/managestransactions.hpp connectionresolverinterface.hpp connectors/connectionfactory.hpp connectors/connector.hpp @@ -130,6 +131,7 @@ function(tiny_sources out_headers out_sources) concerns/detectslostconnections.cpp concerns/hasconnectionresolver.cpp concerns/logsqueries.cpp + concerns/managestransactions.cpp connectors/connectionfactory.cpp connectors/connector.cpp connectors/mysqlconnector.cpp diff --git a/include/include.pri b/include/include.pri index eb15df842..450f4cefb 100644 --- a/include/include.pri +++ b/include/include.pri @@ -11,6 +11,7 @@ headersList += \ $$PWD/orm/concerns/detectslostconnections.hpp \ $$PWD/orm/concerns/hasconnectionresolver.hpp \ $$PWD/orm/concerns/logsqueries.hpp \ + $$PWD/orm/concerns/managestransactions.hpp \ $$PWD/orm/config.hpp \ $$PWD/orm/connectionresolverinterface.hpp \ $$PWD/orm/connectors/connectionfactory.hpp \ diff --git a/include/orm/concerns/countsqueries.hpp b/include/orm/concerns/countsqueries.hpp index 660222d27..3139ec7f3 100644 --- a/include/orm/concerns/countsqueries.hpp +++ b/include/orm/concerns/countsqueries.hpp @@ -5,7 +5,9 @@ #include "orm/macros/systemheader.hpp" TINY_SYSTEM_HEADER -#include +#include + +#include #include "orm/macros/export.hpp" #include "orm/types/statementscounter.hpp" @@ -25,6 +27,9 @@ namespace Concerns { Q_DISABLE_COPY(CountsQueries) + // To access hitTransactionalCounters() method + friend class ManagesTransactions; + public: /*! Default constructor. */ inline CountsQueries() = default; @@ -73,6 +78,10 @@ namespace Concerns StatementsCounter m_statementsCounter {}; private: + /*! Count transactional queries execution time and statements counter. */ + std::optional + hitTransactionalCounters(QElapsedTimer timer, bool countElapsed); + /*! Dynamic cast *this to the DatabaseConnection & derived type. */ DatabaseConnection &databaseConnection(); }; diff --git a/include/orm/concerns/logsqueries.hpp b/include/orm/concerns/logsqueries.hpp index 1e1d376b7..f1ba7b4c1 100644 --- a/include/orm/concerns/logsqueries.hpp +++ b/include/orm/concerns/logsqueries.hpp @@ -7,6 +7,9 @@ TINY_SYSTEM_HEADER #include +#include +#include + #include "orm/macros/export.hpp" #include "orm/types/log.hpp" @@ -72,6 +75,10 @@ namespace Concerns inline static std::atomic m_queryLogId = 0; private: + /*! Convert a named bindings map to the positional bindings vector. */ + QVector + convertNamedToPositionalBindings(QVariantMap &&bindings) const; + /*! Dynamic cast *this to the DatabaseConnection & derived type. */ const DatabaseConnection &databaseConnection() const; diff --git a/include/orm/concerns/managestransactions.hpp b/include/orm/concerns/managestransactions.hpp new file mode 100644 index 000000000..dc492e7c0 --- /dev/null +++ b/include/orm/concerns/managestransactions.hpp @@ -0,0 +1,109 @@ +#pragma once +#ifndef ORM_CONCERNS_MANAGESTRANSACTIONS_HPP +#define ORM_CONCERNS_MANAGESTRANSACTIONS_HPP + +#include "orm/macros/systemheader.hpp" +TINY_SYSTEM_HEADER + +#include + +#include "orm/macros/commonnamespace.hpp" +#include "orm/macros/export.hpp" + +TINYORM_BEGIN_COMMON_NAMESPACE + +namespace Orm +{ + +class DatabaseConnection; +class MySqlConnection; + +namespace Concerns +{ + + class CountsQueries; + + // TODO next transaction method with callback silverqx + // TODO rewrite transactions, look at beginTransaction(), commit(), ... whats up, you will see immediately 😎 silverqx + /*! Manages database transactions. */ + class SHAREDLIB_EXPORT ManagesTransactions + { + Q_DISABLE_COPY(ManagesTransactions) + + // To access resetTransactions() method + friend DatabaseConnection; + // To access resetTransactions() method + friend MySqlConnection; + + public: + /*! Default constructor. */ + ManagesTransactions(); + /*! Virtual destructor, to pass -Weffc++. */ + inline virtual ~ManagesTransactions() = default; + + /*! Start a new database transaction. */ + bool beginTransaction(); + /*! Commit the active database transaction. */ + bool commit(); + /*! Rollback the active database transaction. */ + bool rollBack(); + /*! Start a new named transaction savepoint. */ + bool savepoint(const QString &id); + /*! Start a new named transaction savepoint. */ + bool savepoint(std::size_t id); + /*! Rollback to a named transaction savepoint. */ + bool rollbackToSavepoint(const QString &id); + /*! Rollback to a named transaction savepoint. */ + bool rollbackToSavepoint(std::size_t id); + /*! Get the number of active transactions. */ + inline std::size_t transactionLevel() const; + + /*! Determine whether the database connection is in the transaction state. */ + inline bool inTransaction() const; + + /*! Get namespace prefix for MySQL savepoints. */ + inline const QString &getSavepointNamespace() const; + /*! Set namespace prefix for MySQL savepoints. */ + DatabaseConnection &setSavepointNamespace(const QString &savepointNamespace); + + private: + /*! Reset in transaction state and savepoints. */ + DatabaseConnection &resetTransactions(); + + /*! Dynamic cast *this to the DatabaseConnection & derived type. */ + DatabaseConnection &databaseConnection(); + /*! Dynamic cast *this to the Concerns::CountsQueries & base type. */ + Concerns::CountsQueries &countsQueries(); + + /*! The connection is in the transaction state. */ + bool m_inTransaction = false; + /*! Active savepoints counter. */ + std::size_t m_savepoints = 0; + + /*! Namespace prefix for MySQL savepoints. */ + QString m_savepointNamespace; + }; + + /* public */ + + std::size_t ManagesTransactions::transactionLevel() const + { + return m_savepoints; + } + + bool ManagesTransactions::inTransaction() const + { + return m_inTransaction; + } + + const QString &ManagesTransactions::getSavepointNamespace() const + { + return m_savepointNamespace; + } + +} // namespace Concerns +} // namespace Orm + +TINYORM_END_COMMON_NAMESPACE + +#endif // ORM_CONCERNS_MANAGESTRANSACTIONS_HPP diff --git a/include/orm/databaseconnection.hpp b/include/orm/databaseconnection.hpp index dc3307a77..f32a02018 100644 --- a/include/orm/databaseconnection.hpp +++ b/include/orm/databaseconnection.hpp @@ -5,12 +5,12 @@ #include "orm/macros/systemheader.hpp" TINY_SYSTEM_HEADER -#include #include #include "orm/concerns/countsqueries.hpp" #include "orm/concerns/detectslostconnections.hpp" #include "orm/concerns/logsqueries.hpp" +#include "orm/concerns/managestransactions.hpp" #include "orm/connectors/connectorinterface.hpp" #include "orm/exceptions/queryerror.hpp" #include "orm/query/grammars/grammar.hpp" @@ -52,11 +52,15 @@ namespace Schema /*! Database connection base class. */ class SHAREDLIB_EXPORT DatabaseConnection : public Concerns::DetectsLostConnections, + public Concerns::ManagesTransactions, public Concerns::LogsQueries, public Concerns::CountsQueries { Q_DISABLE_COPY(DatabaseConnection) + // To access shouldCountElapsed() method + friend Concerns::ManagesTransactions; + public: /*! Constructor. */ explicit DatabaseConnection( @@ -83,24 +87,6 @@ namespace Schema /*! Get a new raw query expression. */ inline Query::Expression raw(const QVariant &value) const; - // TODO next transaction method with callback silverqx - /*! Start a new database transaction. */ - bool beginTransaction(); - /*! Commit the active database transaction. */ - bool commit(); - /*! Rollback the active database transaction. */ - bool rollBack(); - /*! Start a new named transaction savepoint. */ - bool savepoint(const QString &id); - /*! Start a new named transaction savepoint. */ - bool savepoint(std::size_t id); - /*! Rollback to a named transaction savepoint. */ - bool rollbackToSavepoint(const QString &id); - /*! Rollback to a named transaction savepoint. */ - bool rollbackToSavepoint(std::size_t id); - /*! Get the number of active transactions. */ - inline std::size_t transactionLevel() const; - /* Running SQL Queries */ /*! Run a select statement against the database. */ QSqlQuery @@ -216,12 +202,6 @@ namespace Schema /*! Reset the record modification state. */ inline void forgetRecordModificationState(); - /*! Get namespace prefix for MySQL savepoints. */ - inline const QString &getSavepointNamespace() const; - /*! Set namespace prefix for MySQL savepoints. */ - inline DatabaseConnection & - setSavepointNamespace(const QString &savepointNamespace); - protected: /*! Set the query grammar to the default implementation. */ void useDefaultQueryGrammar(); @@ -257,14 +237,12 @@ namespace Schema /*! Reconnect to the database if a PDO connection is missing. */ void reconnectIfMissingConnection() const; - /*! Reset in transaction state and savepoints. */ - DatabaseConnection &resetTransactions(); - /*! Log database disconnected, invoked during MySQL ping. */ void logDisconnected(); /*! Log database connected, invoked during MySQL ping. */ void logConnected(); + protected: /*! The active QSqlDatabase connection name. */ std::optional m_qtConnection = std::nullopt; /*! The QSqlDatabase connection resolver. */ @@ -288,8 +266,6 @@ namespace Schema /* Others */ /*! Indicates if the connection is in a "dry run". */ bool m_pretending = false; - /*! Namespace prefix for MySQL savepoints. */ - QString m_savepointNamespace; private: /*! Prepare an SQL statement and return the query object. */ @@ -308,21 +284,13 @@ namespace Schema const QString &queryString, const QVector &bindings, const RunCallback &callback) const; - /*! Count transactional queries execution time and statements counter. */ - std::optional - hitTransactionalCounters(QElapsedTimer timer, bool countElapsed); - /*! Convert a named bindings map to the positional bindings vector. */ - QVector - convertNamedToPositionalBindings(QVariantMap &&bindings) const; + /*! Determine if the elapsed time for queries should be counted. */ + inline bool shouldCountElapsed() const; /*! The flag for the database was disconnected, used during MySQL ping. */ bool m_disconnectedLogged = false; /*! The flag for the database was connected, used during MySQL ping. */ bool m_connectedLogged = false; - /*! The connection is in the transaction state. */ - bool m_inTransaction = false; - /*! Active savepoints counter. */ - std::size_t m_savepoints = 0; /*! Connection name, obtained from the connection configuration. */ QString m_connectionName; @@ -357,11 +325,6 @@ namespace Schema return Query::Expression(value); } - std::size_t DatabaseConnection::transactionLevel() const - { - return m_savepoints; - } - const std::function & DatabaseConnection::getQtConnectionResolver() const { @@ -403,19 +366,6 @@ namespace Schema m_recordsModified = false; } - const QString &DatabaseConnection::getSavepointNamespace() const - { - return m_savepointNamespace; - } - - DatabaseConnection & - DatabaseConnection::setSavepointNamespace(const QString &savepointNamespace) - { - m_savepointNamespace = savepointNamespace; - - return *this; - } - /* protected */ template @@ -427,7 +377,7 @@ namespace Schema reconnectIfMissingConnection(); // Elapsed timer needed - const auto countElapsed = !m_pretending && (m_debugSql || m_countingElapsed); + const auto countElapsed = shouldCountElapsed(); QElapsedTimer timer; if (countElapsed) @@ -488,11 +438,10 @@ namespace Schema const RunCallback &callback) const { // FUTURE add info about in transaction into the exception that it was a reason why connection was not reconnected/recovered silverqx - if (m_inTransaction) + if (inTransaction()) std::rethrow_exception(ePtr); - return tryAgainIfCausedByLostConnection(ePtr, e, queryString, bindings, - callback); + return tryAgainIfCausedByLostConnection(ePtr, e, queryString, bindings, callback); } template @@ -511,6 +460,11 @@ namespace Schema std::rethrow_exception(ePtr); } + bool DatabaseConnection::shouldCountElapsed() const + { + return !m_pretending && (m_debugSql || m_countingElapsed); + } + } // namespace Orm TINYORM_END_COMMON_NAMESPACE diff --git a/src/orm/concerns/countsqueries.cpp b/src/orm/concerns/countsqueries.cpp index 155933a3d..5c5f491df 100644 --- a/src/orm/concerns/countsqueries.cpp +++ b/src/orm/concerns/countsqueries.cpp @@ -7,6 +7,8 @@ TINYORM_BEGIN_COMMON_NAMESPACE namespace Orm::Concerns { +/* public */ + bool CountsQueries::countingElapsed() const { return m_countingElapsed; @@ -107,6 +109,29 @@ DatabaseConnection &CountsQueries::resetStatementsCounter() return databaseConnection(); } +/* private */ + +std::optional +CountsQueries::hitTransactionalCounters(const QElapsedTimer timer, + const bool countElapsed) +{ + std::optional elapsed; + + if (countElapsed) { + // Hit elapsed timer + elapsed = timer.elapsed(); + + // Queries execution time counter + m_elapsedCounter += *elapsed; + } + + // Query statements counter + if (m_countingStatements) + ++m_statementsCounter.transactional; + + return elapsed; +} + DatabaseConnection &CountsQueries::databaseConnection() { return dynamic_cast(*this); diff --git a/src/orm/concerns/logsqueries.cpp b/src/orm/concerns/logsqueries.cpp index e8c0b130f..1e43af458 100644 --- a/src/orm/concerns/logsqueries.cpp +++ b/src/orm/concerns/logsqueries.cpp @@ -1,5 +1,9 @@ #include "orm/concerns/logsqueries.hpp" +#if defined(TINYORM_DEBUG_SQL) +#include +#endif + #include "orm/databaseconnection.hpp" #include "orm/macros/likely.hpp" #ifdef TINYORM_DEBUG_SQL @@ -167,6 +171,18 @@ LogsQueries::withFreshQueryLog(const std::function()> &callback) /* private */ +QVector +LogsQueries::convertNamedToPositionalBindings(QVariantMap &&bindings) const +{ + QVector result; + result.reserve(bindings.size()); + + for (auto &&binding : bindings) + result << std::move(binding); + + return result; +} + const DatabaseConnection &LogsQueries::databaseConnection() const { return dynamic_cast(*this); diff --git a/src/orm/concerns/managestransactions.cpp b/src/orm/concerns/managestransactions.cpp new file mode 100644 index 000000000..ccf00d048 --- /dev/null +++ b/src/orm/concerns/managestransactions.cpp @@ -0,0 +1,248 @@ +#include "orm/concerns/managestransactions.hpp" + +#include "orm/concerns/countsqueries.hpp" +#include "orm/databaseconnection.hpp" +#include "orm/exceptions/sqltransactionerror.hpp" +#include "orm/support/databaseconfiguration.hpp" +#include "orm/utils/type.hpp" + +TINYORM_BEGIN_COMMON_NAMESPACE + +namespace Orm::Concerns +{ + +/* public */ + +ManagesTransactions::ManagesTransactions() + : m_savepointNamespace(Support::DatabaseConfiguration::defaultSavepointNamespace) +{} + +bool ManagesTransactions::beginTransaction() +{ + Q_ASSERT(m_inTransaction == false); + + static const auto query = QStringLiteral("START TRANSACTION"); + + // Elapsed timer needed + const auto countElapsed = databaseConnection().shouldCountElapsed(); + + QElapsedTimer timer; + if (countElapsed) + timer.start(); + + if (!databaseConnection().pretending() && + !databaseConnection().getQtConnection().transaction() + ) + throw Exceptions::SqlTransactionError( + QStringLiteral("Statement in %1() failed : %2") + .arg(__tiny_func__, query), + databaseConnection().getRawQtConnection().lastError()); + + m_inTransaction = true; + + // Queries execution time counter / Query statements counter + auto elapsed = countsQueries().hitTransactionalCounters(timer, countElapsed); + + /* Once we have run the transaction query we will calculate the time + that it took to run and then log the query and execution time. + We'll log time in milliseconds. */ + if (databaseConnection().pretending()) + databaseConnection().logTransactionQueryForPretend(query); + else + databaseConnection().logTransactionQuery(query, std::move(elapsed)); + + return true; +} + +bool ManagesTransactions::commit() +{ + Q_ASSERT(m_inTransaction); + + static const auto query = QStringLiteral("COMMIT"); + + // Elapsed timer needed + const auto countElapsed = databaseConnection().shouldCountElapsed(); + + QElapsedTimer timer; + if (countElapsed) + timer.start(); + + if (!databaseConnection().pretending() && + !databaseConnection().getQtConnection().commit() + ) + throw Exceptions::SqlTransactionError( + QStringLiteral("Statement in %1() failed : %2") + .arg(__tiny_func__, query), + databaseConnection().getRawQtConnection().lastError()); + + m_inTransaction = false; + + // Queries execution time counter / Query statements counter + auto elapsed = countsQueries().hitTransactionalCounters(timer, countElapsed); + + /* Once we have run the transaction query we will calculate the time + that it took to run and then log the query and execution time. + We'll log time in milliseconds. */ + if (databaseConnection().pretending()) + databaseConnection().logTransactionQueryForPretend(query); + else + databaseConnection().logTransactionQuery(query, std::move(elapsed)); + + return true; +} + +bool ManagesTransactions::rollBack() +{ + Q_ASSERT(m_inTransaction); + + static const auto query = QStringLiteral("ROLLBACK"); + + // Elapsed timer needed + const auto countElapsed = databaseConnection().shouldCountElapsed(); + + QElapsedTimer timer; + if (countElapsed) + timer.start(); + + if (!databaseConnection().pretending() && + !databaseConnection().getQtConnection().rollback() + ) + throw Exceptions::SqlTransactionError( + QStringLiteral("Statement in %1() failed : %2") + .arg(__tiny_func__, query), + databaseConnection().getRawQtConnection().lastError()); + + m_inTransaction = false; + + // Queries execution time counter / Query statements counter + auto elapsed = countsQueries().hitTransactionalCounters(timer, countElapsed); + + /* Once we have run the transaction query we will calculate the time + that it took to run and then log the query and execution time. + We'll log time in milliseconds. */ + if (databaseConnection().pretending()) + databaseConnection().logTransactionQueryForPretend(query); + else + databaseConnection().logTransactionQuery(query, std::move(elapsed)); + + return true; +} + +bool ManagesTransactions::savepoint(const QString &id) +{ + // TODO rewrite savepoint() and rollBack() with a new m_connection.statement() API silverqx + Q_ASSERT(m_inTransaction); + + auto savePoint = databaseConnection().getQtQuery(); + const auto query = QStringLiteral("SAVEPOINT %1_%2").arg(m_savepointNamespace, id); + + // Elapsed timer needed + const auto countElapsed = databaseConnection().shouldCountElapsed(); + + QElapsedTimer timer; + if (countElapsed) + timer.start(); + + // Execute a savepoint query + if (!databaseConnection().pretending() && !savePoint.exec(query)) + throw Exceptions::SqlTransactionError( + QStringLiteral("Statement in %1() failed : %2") + .arg(__tiny_func__, query), + savePoint.lastError()); + + ++m_savepoints; + + // Queries execution time counter / Query statements counter + auto elapsed = countsQueries().hitTransactionalCounters(timer, countElapsed); + + /* Once we have run the transaction query we will calculate the time + that it took to run and then log the query and execution time. + We'll log time in milliseconds. */ + if (databaseConnection().pretending()) + databaseConnection().logTransactionQueryForPretend(query); + else + databaseConnection().logTransactionQuery(query, std::move(elapsed)); + + return true; +} + +bool ManagesTransactions::savepoint(const std::size_t id) +{ + return savepoint(QString::number(id)); +} + +bool ManagesTransactions::rollbackToSavepoint(const QString &id) +{ + Q_ASSERT(m_inTransaction); + Q_ASSERT(m_savepoints > 0); + + auto rollbackToSavepoint = databaseConnection().getQtQuery(); + const auto query = QStringLiteral("ROLLBACK TO SAVEPOINT %1_%2") + .arg(m_savepointNamespace, id); + + // Elapsed timer needed + const auto countElapsed = databaseConnection().shouldCountElapsed(); + + QElapsedTimer timer; + if (countElapsed) + timer.start(); + + // Execute a rollback to savepoint query + if (!databaseConnection().pretending() && !rollbackToSavepoint.exec(query)) + throw Exceptions::SqlTransactionError( + QStringLiteral("Statement in %1() failed : %2") + .arg(__tiny_func__, query), + rollbackToSavepoint.lastError()); + + m_savepoints = std::max(0, m_savepoints - 1); + + // Queries execution time counter / Query statements counter + auto elapsed = countsQueries().hitTransactionalCounters(timer, countElapsed); + + /* Once we have run the transaction query we will calculate the time + that it took to run and then log the query and execution time. + We'll log time in milliseconds. */ + if (databaseConnection().pretending()) + databaseConnection().logTransactionQueryForPretend(query); + else + databaseConnection().logTransactionQuery(query, std::move(elapsed)); + + return true; +} + +bool ManagesTransactions::rollbackToSavepoint(const std::size_t id) +{ + return rollbackToSavepoint(QString::number(id)); +} + +DatabaseConnection & +ManagesTransactions::setSavepointNamespace(const QString &savepointNamespace) +{ + m_savepointNamespace = savepointNamespace; + + return databaseConnection(); +} + +/* private */ + +DatabaseConnection &ManagesTransactions::resetTransactions() +{ + m_savepoints = 0; + m_inTransaction = false; + + return databaseConnection(); +} + +DatabaseConnection &ManagesTransactions::databaseConnection() +{ + return dynamic_cast(*this); +} + +CountsQueries &ManagesTransactions::countsQueries() +{ + return dynamic_cast(*this); +} + +} // namespace Orm::Concerns + +TINYORM_END_COMMON_NAMESPACE diff --git a/src/orm/databaseconnection.cpp b/src/orm/databaseconnection.cpp index d42506726..c3c3664e4 100644 --- a/src/orm/databaseconnection.cpp +++ b/src/orm/databaseconnection.cpp @@ -1,13 +1,11 @@ #include "orm/databaseconnection.hpp" #include -#if defined(TINYORM_DEBUG_SQL) || defined(TINYORM_MYSQL_PING) +#if defined(TINYORM_MYSQL_PING) #include #endif -#include "orm/exceptions/sqltransactionerror.hpp" #include "orm/query/querybuilder.hpp" -#include "orm/support/databaseconfiguration.hpp" #include "orm/utils/type.hpp" TINYORM_BEGIN_COMMON_NAMESPACE @@ -45,7 +43,6 @@ DatabaseConnection::DatabaseConnection( , m_database(database) , m_tablePrefix(tablePrefix) , m_config(config) - , m_savepointNamespace(Support::DatabaseConfiguration::defaultSavepointNamespace) , m_connectionName(getConfig(NAME).value()) , m_hostName(getConfig(host_).value()) {} @@ -81,199 +78,6 @@ QSharedPointer DatabaseConnection::query() return QSharedPointer::create(*this, *m_queryGrammar); } -bool DatabaseConnection::beginTransaction() -{ - Q_ASSERT(m_inTransaction == false); - - static const auto query = QStringLiteral("START TRANSACTION"); - - // Elapsed timer needed - const auto countElapsed = !m_pretending && (m_debugSql || m_countingElapsed); - - QElapsedTimer timer; - if (countElapsed) - timer.start(); - - if (!m_pretending && !getQtConnection().transaction()) - throw Exceptions::SqlTransactionError( - QStringLiteral("Statement in %1() failed : %2") - .arg(__tiny_func__, query), - getRawQtConnection().lastError()); - - m_inTransaction = true; - - // Queries execution time counter / Query statements counter - auto elapsed = hitTransactionalCounters(timer, countElapsed); - - /* Once we have run the transaction query we will calculate the time - that it took to run and then log the query and execution time. - We'll log time in milliseconds. */ - if (m_pretending) - logTransactionQueryForPretend(query); - else - logTransactionQuery(query, std::move(elapsed)); - - return true; -} - -bool DatabaseConnection::commit() -{ - Q_ASSERT(m_inTransaction); - - static const auto query = QStringLiteral("COMMIT"); - - // Elapsed timer needed - const auto countElapsed = !m_pretending && (m_debugSql || m_countingElapsed); - - QElapsedTimer timer; - if (countElapsed) - timer.start(); - - // TODO rewrite transactions to DatabaseConnection::statement, so I have access to QSqlQuery for logQuery() silverqx - if (!m_pretending && !getQtConnection().commit()) - throw Exceptions::SqlTransactionError( - QStringLiteral("Statement in %1() failed : %2") - .arg(__tiny_func__, query), - getRawQtConnection().lastError()); - - m_inTransaction = false; - - // Queries execution time counter / Query statements counter - auto elapsed = hitTransactionalCounters(timer, countElapsed); - - /* Once we have run the transaction query we will calculate the time - that it took to run and then log the query and execution time. - We'll log time in milliseconds. */ - if (m_pretending) - logTransactionQueryForPretend(query); - else - logTransactionQuery(query, std::move(elapsed)); - - return true; -} - -bool DatabaseConnection::rollBack() -{ - Q_ASSERT(m_inTransaction); - - static const auto query = QStringLiteral("ROLLBACK"); - - // Elapsed timer needed - const auto countElapsed = !m_pretending && (m_debugSql || m_countingElapsed); - - QElapsedTimer timer; - if (countElapsed) - timer.start(); - - if (!m_pretending && !getQtConnection().rollback()) - throw Exceptions::SqlTransactionError( - QStringLiteral("Statement in %1() failed : %2") - .arg(__tiny_func__, query), - getRawQtConnection().lastError()); - - m_inTransaction = false; - - // Queries execution time counter / Query statements counter - auto elapsed = hitTransactionalCounters(timer, countElapsed); - - /* Once we have run the transaction query we will calculate the time - that it took to run and then log the query and execution time. - We'll log time in milliseconds. */ - if (m_pretending) - logTransactionQueryForPretend(query); - else - logTransactionQuery(query, std::move(elapsed)); - - return true; -} - -bool DatabaseConnection::savepoint(const QString &id) -{ - // TODO rewrite savepoint() and rollBack() with a new m_connection.statement() API silverqx - Q_ASSERT(m_inTransaction); - - auto savePoint = getQtQuery(); - const auto query = QStringLiteral("SAVEPOINT %1_%2").arg(m_savepointNamespace, id); - - // Elapsed timer needed - const auto countElapsed = !m_pretending && (m_debugSql || m_countingElapsed); - - QElapsedTimer timer; - if (countElapsed) - timer.start(); - - // Execute a savepoint query - if (!m_pretending && !savePoint.exec(query)) - throw Exceptions::SqlTransactionError( - QStringLiteral("Statement in %1() failed : %2") - .arg(__tiny_func__, query), - savePoint.lastError()); - - ++m_savepoints; - - // Queries execution time counter / Query statements counter - auto elapsed = hitTransactionalCounters(timer, countElapsed); - - /* Once we have run the transaction query we will calculate the time - that it took to run and then log the query and execution time. - We'll log time in milliseconds. */ - if (m_pretending) - logTransactionQueryForPretend(query); - else - logTransactionQuery(query, std::move(elapsed)); - - return true; -} - -bool DatabaseConnection::savepoint(const std::size_t id) -{ - return savepoint(QString::number(id)); -} - -bool DatabaseConnection::rollbackToSavepoint(const QString &id) -{ - Q_ASSERT(m_inTransaction); - Q_ASSERT(m_savepoints > 0); - - auto rollbackToSavepoint = getQtQuery(); - const auto query = QStringLiteral("ROLLBACK TO SAVEPOINT %1_%2") - .arg(m_savepointNamespace, id); - - // Elapsed timer needed - const auto countElapsed = !m_pretending && (m_debugSql || m_countingElapsed); - - QElapsedTimer timer; - if (countElapsed) - timer.start(); - - // Execute a rollback to savepoint query - if (!m_pretending && !rollbackToSavepoint.exec(query)) - throw Exceptions::SqlTransactionError( - QStringLiteral("Statement in %1() failed : %2") - .arg(__tiny_func__, query), - rollbackToSavepoint.lastError()); - - m_savepoints = std::max(0, m_savepoints - 1); - - // Queries execution time counter / Query statements counter - auto elapsed = hitTransactionalCounters(timer, countElapsed); - - /* Once we have run the transaction query we will calculate the time - that it took to run and then log the query and execution time. - We'll log time in milliseconds. */ - if (m_pretending) - logTransactionQueryForPretend(query); - else - logTransactionQuery(query, std::move(elapsed)); - - return true; -} - -bool DatabaseConnection::rollbackToSavepoint(const std::size_t id) -{ - return rollbackToSavepoint(QString::number(id)); -} - /* Running SQL Queries */ QSqlQuery @@ -578,27 +382,6 @@ void DatabaseConnection::bindValues(QSqlQuery &query, } } -std::optional -DatabaseConnection::hitTransactionalCounters(const QElapsedTimer timer, - const bool countElapsed) -{ - std::optional elapsed; - - if (countElapsed) { - // Hit elapsed timer - elapsed = timer.elapsed(); - - // Queries execution time counter - m_elapsedCounter += *elapsed; - } - - // Query statements counter - if (m_countingStatements) - ++m_statementsCounter.transactional; - - return elapsed; -} - bool DatabaseConnection::pingDatabase() { throw Exceptions::RuntimeError( @@ -771,16 +554,9 @@ void DatabaseConnection::reconnectIfMissingConnection() const } } -DatabaseConnection &DatabaseConnection::resetTransactions() -{ - m_savepoints = 0; - m_inTransaction = false; - - return *this; -} - void DatabaseConnection::logDisconnected() { +#ifdef TINYORM_MYSQL_PING if (m_disconnectedLogged) return; @@ -794,10 +570,12 @@ void DatabaseConnection::logDisconnected() m_connectionName.toUtf8().constData(), m_hostName.toUtf8().constData(), m_database.toUtf8().constData()); +#endif } void DatabaseConnection::logConnected() { +#ifdef TINYORM_MYSQL_PING if (m_connectedLogged) return; @@ -806,11 +584,14 @@ void DatabaseConnection::logConnected() // Reset disconnected flag m_disconnectedLogged = false; + /* I still don't know if it's a good idea to log this to the console by default, + it's a very important log message though. */ qInfo("%s database connected (%s, %s@%s)", driverNamePrintable().toUtf8().constData(), m_connectionName.toUtf8().constData(), m_hostName.toUtf8().constData(), m_database.toUtf8().constData()); +#endif } /* private */ @@ -828,18 +609,6 @@ QSqlQuery DatabaseConnection::prepareQuery(const QString &queryString) return query; } -QVector -DatabaseConnection::convertNamedToPositionalBindings(QVariantMap &&bindings) const -{ - QVector result; - result.reserve(bindings.size()); - - for (auto &&binding : bindings) - result << std::move(binding); - - return result; -} - } // namespace Orm TINYORM_END_COMMON_NAMESPACE diff --git a/src/src.pri b/src/src.pri index eb21e5689..5bc2b5d24 100644 --- a/src/src.pri +++ b/src/src.pri @@ -7,6 +7,7 @@ sourcesList += \ $$PWD/orm/concerns/detectslostconnections.cpp \ $$PWD/orm/concerns/hasconnectionresolver.cpp \ $$PWD/orm/concerns/logsqueries.cpp \ + $$PWD/orm/concerns/managestransactions.cpp \ $$PWD/orm/connectors/connectionfactory.cpp \ $$PWD/orm/connectors/connector.cpp \ $$PWD/orm/connectors/mysqlconnector.cpp \