mirror of
https://github.com/silverqx/TinyORM.git
synced 2026-01-06 02:49:31 -06:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include "orm/macros/systemheader.hpp"
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#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<qint64>
|
||||
hitTransactionalCounters(QElapsedTimer timer, bool countElapsed);
|
||||
|
||||
/*! Dynamic cast *this to the DatabaseConnection & derived type. */
|
||||
DatabaseConnection &databaseConnection();
|
||||
};
|
||||
|
||||
@@ -7,6 +7,9 @@ TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QtSql/QSqlQuery>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "orm/macros/export.hpp"
|
||||
#include "orm/types/log.hpp"
|
||||
|
||||
@@ -72,6 +75,10 @@ namespace Concerns
|
||||
inline static std::atomic<std::size_t> m_queryLogId = 0;
|
||||
|
||||
private:
|
||||
/*! Convert a named bindings map to the positional bindings vector. */
|
||||
QVector<QVariant>
|
||||
convertNamedToPositionalBindings(QVariantMap &&bindings) const;
|
||||
|
||||
/*! Dynamic cast *this to the DatabaseConnection & derived type. */
|
||||
const DatabaseConnection &databaseConnection() const;
|
||||
|
||||
|
||||
109
include/orm/concerns/managestransactions.hpp
Normal file
109
include/orm/concerns/managestransactions.hpp
Normal file
@@ -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 <QString>
|
||||
|
||||
#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
|
||||
@@ -5,12 +5,12 @@
|
||||
#include "orm/macros/systemheader.hpp"
|
||||
TINY_SYSTEM_HEADER
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QtSql/QSqlDatabase>
|
||||
|
||||
#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<Connectors::ConnectionName> 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<QVariant> &bindings,
|
||||
const RunCallback<Return> &callback) const;
|
||||
|
||||
/*! Count transactional queries execution time and statements counter. */
|
||||
std::optional<qint64>
|
||||
hitTransactionalCounters(QElapsedTimer timer, bool countElapsed);
|
||||
/*! Convert a named bindings map to the positional bindings vector. */
|
||||
QVector<QVariant>
|
||||
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<Connectors::ConnectionName()> &
|
||||
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<typename Return>
|
||||
@@ -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<Return> &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<typename Return>
|
||||
@@ -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
|
||||
|
||||
@@ -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<qint64>
|
||||
CountsQueries::hitTransactionalCounters(const QElapsedTimer timer,
|
||||
const bool countElapsed)
|
||||
{
|
||||
std::optional<qint64> 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<DatabaseConnection &>(*this);
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "orm/concerns/logsqueries.hpp"
|
||||
|
||||
#if defined(TINYORM_DEBUG_SQL)
|
||||
#include <QDebug>
|
||||
#endif
|
||||
|
||||
#include "orm/databaseconnection.hpp"
|
||||
#include "orm/macros/likely.hpp"
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
@@ -167,6 +171,18 @@ LogsQueries::withFreshQueryLog(const std::function<QVector<Log>()> &callback)
|
||||
|
||||
/* private */
|
||||
|
||||
QVector<QVariant>
|
||||
LogsQueries::convertNamedToPositionalBindings(QVariantMap &&bindings) const
|
||||
{
|
||||
QVector<QVariant> result;
|
||||
result.reserve(bindings.size());
|
||||
|
||||
for (auto &&binding : bindings)
|
||||
result << std::move(binding);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const DatabaseConnection &LogsQueries::databaseConnection() const
|
||||
{
|
||||
return dynamic_cast<const DatabaseConnection &>(*this);
|
||||
|
||||
248
src/orm/concerns/managestransactions.cpp
Normal file
248
src/orm/concerns/managestransactions.cpp
Normal file
@@ -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<std::size_t>(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<DatabaseConnection &>(*this);
|
||||
}
|
||||
|
||||
CountsQueries &ManagesTransactions::countsQueries()
|
||||
{
|
||||
return dynamic_cast<CountsQueries &>(*this);
|
||||
}
|
||||
|
||||
} // namespace Orm::Concerns
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
@@ -1,13 +1,11 @@
|
||||
#include "orm/databaseconnection.hpp"
|
||||
|
||||
#include <QDateTime>
|
||||
#if defined(TINYORM_DEBUG_SQL) || defined(TINYORM_MYSQL_PING)
|
||||
#if defined(TINYORM_MYSQL_PING)
|
||||
#include <QDebug>
|
||||
#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<QString>())
|
||||
, m_hostName(getConfig(host_).value<QString>())
|
||||
{}
|
||||
@@ -81,199 +78,6 @@ QSharedPointer<QueryBuilder> DatabaseConnection::query()
|
||||
return QSharedPointer<QueryBuilder>::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<std::size_t>(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<qint64>
|
||||
DatabaseConnection::hitTransactionalCounters(const QElapsedTimer timer,
|
||||
const bool countElapsed)
|
||||
{
|
||||
std::optional<qint64> 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<QVariant>
|
||||
DatabaseConnection::convertNamedToPositionalBindings(QVariantMap &&bindings) const
|
||||
{
|
||||
QVector<QVariant> result;
|
||||
result.reserve(bindings.size());
|
||||
|
||||
for (auto &&binding : bindings)
|
||||
result << std::move(binding);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Orm
|
||||
|
||||
TINYORM_END_COMMON_NAMESPACE
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user