mirror of
https://github.com/silverqx/TinyORM.git
synced 2026-01-06 02:49:31 -06:00
added BelongsToMany::attach and queries counter
The BelongsToMany::attach overloads are able to accept also Model instance/s. The many-to-many relation also supports touching timestamps on the pivot table and also touching parent timestamps. Added queries execution time counter and the number of executed queries counter, it counts normal, affecting and transactional queries. Also added proxy methods to the DB facade and DatabaseManager. The DatabaseManager also contains api to be able enable/disable counters on all connections or get/reset counters on all connections. - tried to compile without PCH and added all the missing includes
This commit is contained in:
@@ -34,6 +34,7 @@ Todo categories:
|
||||
Common:
|
||||
|
||||
- api different : different api than Laravel's Eloquent
|
||||
- check : something to find out 🤔
|
||||
- docs : document code or update markdown documentation
|
||||
- desirable : feature which is extremely wanted
|
||||
- dilemma : some sort of a fuckup
|
||||
@@ -44,8 +45,10 @@ Common:
|
||||
- mystery : don't know why that stuff is happening, find out what's up
|
||||
- now : do it before commit
|
||||
- next : next thing in the row to do after commit
|
||||
- overflow : add check code, eg when size_t to int coversion
|
||||
- perf : performance
|
||||
- production : check before deploy to production
|
||||
- reliability : make things more robust and reliable
|
||||
- security : self explaining
|
||||
- test : tasks in auto tests
|
||||
- types : juggling with c++ types
|
||||
@@ -56,6 +59,7 @@ Features related/to implement:
|
||||
- guarded : related to the mass assignable feature
|
||||
- logging : logging related
|
||||
- multidriver : task related to adding support for another drivers PostgreSQL, SQLite and SQL Server
|
||||
- pivot : pivot table in the many-to-many relationship
|
||||
- postgres : specific to PostgreSQL server
|
||||
- primarykey dilema : different types for primary keys
|
||||
- relations : relations related 🤓
|
||||
|
||||
@@ -18,6 +18,7 @@ HEADERS += \
|
||||
$$PWD/orm/invalidformaterror.hpp \
|
||||
$$PWD/orm/logquery.hpp \
|
||||
$$PWD/orm/mysqlconnection.hpp \
|
||||
$$PWD/orm/ormdomainerror.hpp \
|
||||
$$PWD/orm/ormlogicerror.hpp \
|
||||
$$PWD/orm/ormruntimeerror.hpp \
|
||||
$$PWD/orm/ormtypes.hpp \
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef CONFIGURATION_H
|
||||
#define CONFIGURATION_H
|
||||
|
||||
#include <QVariantHash>
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
namespace TINYORM_COMMON_NAMESPACE
|
||||
{
|
||||
|
||||
@@ -12,7 +12,18 @@ namespace TINYORM_COMMON_NAMESPACE
|
||||
namespace Orm
|
||||
{
|
||||
|
||||
class DatabaseConnection;
|
||||
class Grammar;
|
||||
/*! Counts executed statements in a current connection. */
|
||||
struct StatementsCounter
|
||||
{
|
||||
/*! Normal select statements. */
|
||||
int normal = -1;
|
||||
/*! Affecting statements (UPDATE, INSERT, DELETE). */
|
||||
int affecting = -1;
|
||||
/*! Transactional statements (START TRANSACTION, ROLLBACK, COMMIT, SAVEPOINT). */
|
||||
int transactional = -1;
|
||||
};
|
||||
|
||||
namespace Query
|
||||
{
|
||||
@@ -121,6 +132,34 @@ namespace Query
|
||||
|
||||
/*! Get the query grammar used by the connection. */
|
||||
virtual const Grammar &getQueryGrammar() const = 0;
|
||||
|
||||
/* Queries execution time counter */
|
||||
/*! Determine whether we're counting queries execution time. */
|
||||
virtual bool countingElapsed() const = 0;
|
||||
/*! Enable counting queries execution time on the current connection. */
|
||||
virtual DatabaseConnection &enableElapsedCounter() = 0;
|
||||
/*! Disable counting queries execution time on the current connection. */
|
||||
virtual DatabaseConnection &disableElapsedCounter() = 0;
|
||||
/*! Obtain queries execution time. */
|
||||
virtual qint64 getElapsedCounter() const = 0;
|
||||
/*! Obtain and reset queries execution time. */
|
||||
virtual qint64 takeElapsedCounter() = 0;
|
||||
/*! Reset queries execution time. */
|
||||
virtual DatabaseConnection &resetElapsedCounter() = 0;
|
||||
|
||||
/* Queries executed counter */
|
||||
/*! Determine whether we're counting the number of executed queries. */
|
||||
virtual bool countingStatements() const = 0;
|
||||
/*! Enable counting the number of executed queries on the current connection. */
|
||||
virtual DatabaseConnection &enableStatementsCounter() = 0;
|
||||
/*! Disable counting the number of executed queries on the current connection. */
|
||||
virtual DatabaseConnection &disableStatementsCounter() = 0;
|
||||
/*! Obtain the number of executed queries. */
|
||||
virtual const StatementsCounter &getStatementsCounter() const = 0;
|
||||
/*! Obtain and reset the number of executed queries. */
|
||||
virtual StatementsCounter takeStatementsCounter() = 0;
|
||||
/*! Reset the number of executed queries. */
|
||||
virtual DatabaseConnection &resetStatementsCounter() = 0;
|
||||
};
|
||||
|
||||
} // namespace Orm
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define CONNECTOR_HPP
|
||||
|
||||
#include <QtSql/QSqlDatabase>
|
||||
#include <QVariantHash>
|
||||
|
||||
#include "orm/concerns/detectslostconnections.hpp"
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef CONNECTORINTERFACE_HPP
|
||||
#define CONNECTORINTERFACE_HPP
|
||||
|
||||
#include <QVariantHash>
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
namespace TINYORM_COMMON_NAMESPACE
|
||||
{
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <QElapsedTimer>
|
||||
#include <QtSql/QSqlDatabase>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "orm/concerns/detectslostconnections.hpp"
|
||||
#include "orm/connectioninterface.hpp"
|
||||
#include "orm/connectors/connectorinterface.hpp"
|
||||
@@ -117,7 +119,8 @@ namespace Orm
|
||||
const QVector<QVariant> &bindings) const;
|
||||
/*! Log a query into the connection's query log. */
|
||||
void logQuery(const QSqlQuery &query,
|
||||
const std::optional<quint64> elapsed) const;
|
||||
// CUR elapsed should be qint64 silverqx
|
||||
const std::optional<quint64> &elapsed);
|
||||
|
||||
/*! Check database connection and show warnings when the state changed. */
|
||||
bool pingDatabase() override;
|
||||
@@ -148,6 +151,34 @@ namespace Orm
|
||||
/*! Get the configuration for the current connection. */
|
||||
QVariant getConfig() const;
|
||||
|
||||
/* Queries execution time counter */
|
||||
/*! Determine whether we're counting queries execution time. */
|
||||
bool countingElapsed() const override;
|
||||
/*! Enable counting queries execution time on the current connection. */
|
||||
DatabaseConnection &enableElapsedCounter() override;
|
||||
/*! Disable counting queries execution time on the current connection. */
|
||||
DatabaseConnection &disableElapsedCounter() override;
|
||||
/*! Obtain queries execution time. */
|
||||
qint64 getElapsedCounter() const override;
|
||||
/*! Obtain and reset queries execution time. */
|
||||
qint64 takeElapsedCounter() override;
|
||||
/*! Reset queries execution time. */
|
||||
DatabaseConnection &resetElapsedCounter() override;
|
||||
|
||||
/* Queries executed counter */
|
||||
/*! Determine whether we're counting the number of executed queries. */
|
||||
bool countingStatements() const override;
|
||||
/*! Enable counting the number of executed queries on the current connection. */
|
||||
DatabaseConnection &enableStatementsCounter() override;
|
||||
/*! Disable counting the number of executed queries on the current connection. */
|
||||
DatabaseConnection &disableStatementsCounter() override;
|
||||
/*! Obtain the number of executed queries. */
|
||||
const StatementsCounter &getStatementsCounter() const override;
|
||||
/*! Obtain and reset the number of executed queries. */
|
||||
StatementsCounter takeStatementsCounter() override;
|
||||
/*! Reset the number of executed queries. */
|
||||
DatabaseConnection &resetStatementsCounter() override;
|
||||
|
||||
protected:
|
||||
/*! Callback type used in the run() methods. */
|
||||
template<typename Result>
|
||||
@@ -158,7 +189,7 @@ namespace Orm
|
||||
template<typename Result>
|
||||
std::tuple<Result, QSqlQuery>
|
||||
run(const QString &queryString, const QVector<QVariant> &bindings,
|
||||
const RunCallback<Result> &callback) const;
|
||||
const RunCallback<Result> &callback);
|
||||
/*! Run a SQL statement. */
|
||||
template<typename Result>
|
||||
std::tuple<Result, QSqlQuery>
|
||||
@@ -185,6 +216,18 @@ namespace Orm
|
||||
/*! The reconnector instance for the connection. */
|
||||
ReconnectorType m_reconnector;
|
||||
|
||||
/* Queries execution time counter */
|
||||
/*! Indicates whether queries elapsed time are being counted. */
|
||||
bool m_countingElapsed = false;
|
||||
/*! Queries elpased time counter. */
|
||||
qint64 m_elapsedCounter = -1;
|
||||
|
||||
/* Queries executed counter */
|
||||
/*! Indicates whether executed queries are being counted. */
|
||||
bool m_countingStatements = false;
|
||||
/*! Counts executed statements on current connection. */
|
||||
StatementsCounter m_statementsCounter;
|
||||
|
||||
private:
|
||||
/*! Namespace prefix for MySQL savepoints. */
|
||||
static const char *SAVEPOINT_NAMESPACE;
|
||||
@@ -212,7 +255,10 @@ namespace Orm
|
||||
void logConnected();
|
||||
/*! Log a transaction query into the connection's query log. */
|
||||
void logTransactionQuery(const QString &query,
|
||||
const std::optional<quint64> elapsed) const;
|
||||
const std::optional<quint64> elapsed);
|
||||
/*! Count transactional queries execution time and statements counter. */
|
||||
std::optional<quint64>
|
||||
hitTransactionalCounters(const QElapsedTimer &timer);
|
||||
|
||||
/*! The flag for the database was disconnected. */
|
||||
bool m_disconnectedLogged = false;
|
||||
@@ -224,23 +270,31 @@ namespace Orm
|
||||
uint m_savepoints = 0;
|
||||
/*! The query grammar implementation. */
|
||||
Grammar m_queryGrammar;
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
/*! Indicates whether logging of sql queries is enabled. */
|
||||
const bool m_debugSql = true;
|
||||
#else
|
||||
/*! Indicates whether logging of sql queries is enabled. */
|
||||
const bool m_debugSql = false;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
template<typename Result>
|
||||
std::tuple<Result, QSqlQuery>
|
||||
DatabaseConnection::run(
|
||||
const QString &queryString, const QVector<QVariant> &bindings,
|
||||
const RunCallback<Result> &callback) const
|
||||
const RunCallback<Result> &callback)
|
||||
{
|
||||
reconnectIfMissingConnection();
|
||||
|
||||
Result result;
|
||||
QSqlQuery query;
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
#endif
|
||||
if (m_debugSql || m_countingElapsed)
|
||||
timer.start();
|
||||
|
||||
/* Here we will run this query. If an exception occurs we'll determine if it was
|
||||
caused by a connection that has been lost. If that is the cause, we'll try
|
||||
@@ -254,11 +308,19 @@ namespace Orm
|
||||
handleQueryException(e, queryString, bindings, callback);
|
||||
}
|
||||
|
||||
std::optional<quint64> elapsed;
|
||||
if (m_debugSql || m_countingElapsed)
|
||||
elapsed = timer.elapsed();
|
||||
|
||||
// Queries execution time counter
|
||||
if (m_countingElapsed)
|
||||
m_elapsedCounter += *elapsed;
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
/* Once we have run the query we will calculate the time that it took
|
||||
to run and then log the query, bindings, and execution time. We'll
|
||||
log time in milliseconds. */
|
||||
logQuery(query, timer.elapsed());
|
||||
logQuery(query, elapsed);
|
||||
#endif
|
||||
|
||||
return {result, query};
|
||||
|
||||
@@ -108,7 +108,9 @@ namespace Query
|
||||
void disconnect(QString name = "") const;
|
||||
|
||||
/*! Get all of the support drivers. */
|
||||
const QStringList supportedDrivers() const;
|
||||
QStringList supportedDrivers() const;
|
||||
/*! Returns a list containing the names of all connections. */
|
||||
QStringList connectionNames() const;
|
||||
|
||||
/*! Get the default connection name. */
|
||||
const QString &getDefaultConnection() const override;
|
||||
@@ -121,6 +123,66 @@ namespace Query
|
||||
/*! Set the database reconnector callback. */
|
||||
DatabaseManager &setReconnector(const ReconnectorType &reconnector);
|
||||
|
||||
/* Queries execution time counter */
|
||||
/*! Determine whether we're counting queries execution time. */
|
||||
bool countingElapsed(const QString &connection = "");
|
||||
/*! Enable counting queries execution time on the current connection. */
|
||||
DatabaseConnection &enableElapsedCounter(const QString &connection = "");
|
||||
/*! Disable counting queries execution time on the current connection. */
|
||||
DatabaseConnection &disableElapsedCounter(const QString &connection = "");
|
||||
/*! Obtain queries execution time. */
|
||||
qint64 getElapsedCounter(const QString &connection = "");
|
||||
/*! Obtain and reset queries execution time. */
|
||||
qint64 takeElapsedCounter(const QString &connection = "");
|
||||
/*! Reset queries execution time. */
|
||||
DatabaseConnection &resetElapsedCounter(const QString &connection = "");
|
||||
|
||||
/*! Determine whether any connection is counting queries execution time. */
|
||||
bool anyCountingElapsed();
|
||||
/*! Enable counting queries execution time on all connections. */
|
||||
void enableAllElapsedCounters();
|
||||
/*! Disable counting queries execution time on all connections. */
|
||||
void disableAllElapsedCounters();
|
||||
/*! Obtain queries execution time from all connections. */
|
||||
qint64 getAllElapsedCounters();
|
||||
/*! Obtain and reset queries execution time on all active connections. */
|
||||
qint64 takeAllElapsedCounters();
|
||||
/*! Reset queries execution time on all active connections. */
|
||||
void resetAllElapsedCounters();
|
||||
|
||||
/* Queries executed counter */
|
||||
/*! Determine whether we're counting the number of executed queries. */
|
||||
bool countingStatements(const QString &connection = "");
|
||||
/*! Enable counting the number of executed queries on the current connection. */
|
||||
DatabaseConnection &
|
||||
enableStatementsCounter(const QString &connection = "");
|
||||
/*! Disable counting the number of executed queries on the current connection. */
|
||||
DatabaseConnection &
|
||||
disableStatementsCounter(const QString &connection = "");
|
||||
/*! Obtain the number of executed queries. */
|
||||
const StatementsCounter &
|
||||
getStatementsCounter(const QString &connection = "");
|
||||
/*! Obtain and reset the number of executed queries. */
|
||||
StatementsCounter
|
||||
takeStatementsCounter(const QString &connection = "");
|
||||
/*! Reset the number of executed queries. */
|
||||
DatabaseConnection &
|
||||
resetStatementsCounter(const QString &connection = "");
|
||||
|
||||
/*! Determine whether any connection is counting the number of executed
|
||||
queries. */
|
||||
bool anyCountingStatements();
|
||||
/*! Enable counting the number of executed queries on all connections. */
|
||||
void enableAllStatementCounters();
|
||||
/*! Disable counting the number of executed queries on all connections. */
|
||||
void disableAllStatementCounters();
|
||||
/*! Obtain the number of executed queries on all active connections. */
|
||||
StatementsCounter getAllStatementCounters();
|
||||
/*! Obtain and reset the number of executed queries on all active connections. */
|
||||
StatementsCounter takeAllStatementCounters();
|
||||
/*! Reset the number of executed queries on all active connections. */
|
||||
void resetAllStatementCounters();
|
||||
|
||||
protected:
|
||||
/*! Default connection name. */
|
||||
static const char *defaultConnectionName;
|
||||
@@ -154,7 +216,7 @@ namespace Query
|
||||
configure(std::unique_ptr<DatabaseConnection> connection) const;
|
||||
|
||||
/*! Refresh an underlying QSqlDatabase connection on a given connection. */
|
||||
DatabaseConnection &refreshPdoConnections(const QString &name);
|
||||
DatabaseConnection &refreshQtConnections(const QString &name);
|
||||
|
||||
/*! The database connection factory instance. */
|
||||
const Connectors::ConnectionFactory m_factory;
|
||||
|
||||
@@ -56,6 +56,8 @@ namespace Orm
|
||||
|
||||
/*! Get all of the support drivers. */
|
||||
static const QStringList supportedDrivers();
|
||||
/*! Returns a list containing the names of all connections. */
|
||||
static QStringList connectionNames();
|
||||
|
||||
/*! Get the default connection name. */
|
||||
static const QString &getDefaultConnection();
|
||||
@@ -119,6 +121,73 @@ namespace Orm
|
||||
const QString &connection = "");
|
||||
/*! Get the number of active transactions. */
|
||||
static uint transactionLevel(const QString &connection = "");
|
||||
|
||||
/* Queries execution time counter */
|
||||
/*! Determine whether we're counting queries execution time. */
|
||||
static bool
|
||||
countingElapsed(const QString &connection = "");
|
||||
/*! Enable counting queries elapsed time on the current connection. */
|
||||
static DatabaseConnection &
|
||||
enableElapsedCounter(const QString &connection = "");
|
||||
/*! Disable counting queries elapsed time on the current connection. */
|
||||
static DatabaseConnection &
|
||||
disableElapsedCounter(const QString &connection = "");
|
||||
/*! Obtain queries elapsed time. */
|
||||
static qint64
|
||||
getElapsedCounter(const QString &connection = "");
|
||||
/*! Obtain and reset queries elapsed time. */
|
||||
static qint64
|
||||
takeElapsedCounter(const QString &connection = "");
|
||||
/*! Reset queries elapsed time. */
|
||||
static DatabaseConnection &
|
||||
resetElapsedCounter(const QString &connection = "");
|
||||
|
||||
/*! Determine whether any connection is counting queries execution time. */
|
||||
static bool anyCountingElapsed();
|
||||
/*! Enable counting queries execution time on all connections. */
|
||||
static void enableAllElapsedCounters();
|
||||
/*! Disable counting queries execution time on all connections. */
|
||||
static void disableAllElapsedCounters();
|
||||
/*! Obtain queries execution time from all connections. */
|
||||
static qint64 getAllElapsedCounters();
|
||||
/*! Obtain and reset queries execution time on all active connections. */
|
||||
static qint64 takeAllElapsedCounters();
|
||||
/*! Reset queries execution time on all active connections. */
|
||||
static void resetAllElapsedCounters();
|
||||
|
||||
/* Queries executed counter */
|
||||
/*! Determine whether we're counting the number of executed queries. */
|
||||
static bool
|
||||
countingStatements(const QString &connection = "");
|
||||
/*! Enable counting the number of executed queries on the current connection. */
|
||||
static DatabaseConnection &
|
||||
enableStatementsCounter(const QString &connection = "");
|
||||
/*! Disable counting the number of executed queries on the current connection. */
|
||||
static DatabaseConnection &
|
||||
disableStatementsCounter(const QString &connection = "");
|
||||
/*! Obtain the number of executed queries. */
|
||||
static const StatementsCounter &
|
||||
getStatementsCounter(const QString &connection = "");
|
||||
/*! Obtain and reset the number of executed queries. */
|
||||
static StatementsCounter
|
||||
takeStatementsCounter(const QString &connection = "");
|
||||
/*! Reset the number of executed queries. */
|
||||
static DatabaseConnection &
|
||||
resetStatementsCounter(const QString &connection = "");
|
||||
|
||||
/*! Determine whether any connection is counting the number of executed
|
||||
queries. */
|
||||
static bool anyCountingStatements();
|
||||
/*! Enable counting the number of executed queries on all connections. */
|
||||
static void enableAllStatementCounters();
|
||||
/*! Disable counting the number of executed queries on all connections. */
|
||||
static void disableAllStatementCounters();
|
||||
/*! Obtain the number of executed queries on all active connections. */
|
||||
static StatementsCounter getAllStatementCounters();
|
||||
/*! Obtain and reset the number of executed queries on all active connections. */
|
||||
static StatementsCounter takeAllStatementCounters();
|
||||
/*! Reset the number of executed queries on all active connections. */
|
||||
static void resetAllStatementCounters();
|
||||
};
|
||||
|
||||
} // namespace Orm
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef LOGQUERY_H
|
||||
#define LOGQUERY_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class QSqlQuery;
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
|
||||
26
include/orm/ormdomainerror.hpp
Normal file
26
include/orm/ormdomainerror.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef ORMDOMAINERROR_H
|
||||
#define ORMDOMAINERROR_H
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "export.hpp"
|
||||
#include "orm/ormlogicerror.hpp"
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
namespace TINYORM_COMMON_NAMESPACE
|
||||
{
|
||||
#endif
|
||||
namespace Orm
|
||||
{
|
||||
|
||||
class SHAREDLIB_EXPORT OrmDomainError : public OrmLogicError
|
||||
{
|
||||
using OrmLogicError::OrmLogicError;
|
||||
};
|
||||
|
||||
} // namespace Orm
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
} // namespace TINYORM_COMMON_NAMESPACE
|
||||
#endif
|
||||
|
||||
#endif // ORMDOMAINERROR_H
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef ORMRUNTIMEERROR_H
|
||||
#define ORMRUNTIMEERROR_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "export.hpp"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#ifndef ORMTYPES_H
|
||||
#define ORMTYPES_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include "export.hpp"
|
||||
|
||||
// TODO divide OrmTypes to internal and types which user will / may need, so divide to two files silverqx
|
||||
@@ -109,6 +113,7 @@ namespace Query
|
||||
};
|
||||
|
||||
// TODO types, also divide types by namespace, eg AttributeItem is only used in the Orm::Tiny namespace, so an user can use 'using namespace Orm::Tiny' in model files, it is not possible now, because he has to use symbols from an Orm namespace too silverqx
|
||||
// TODO pretty print in the debugger silverqx
|
||||
struct SHAREDLIB_EXPORT AttributeItem
|
||||
{
|
||||
QString key;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef EXPRESSION_H
|
||||
#define EXPRESSION_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QVector>
|
||||
|
||||
#include "export.hpp"
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <QtSql/QSqlQuery>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "orm/query/expression.hpp"
|
||||
#include "orm/ormtypes.hpp"
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef QUERYERROR_H
|
||||
#define QUERYERROR_H
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include "orm/sqlerror.hpp"
|
||||
|
||||
class QSqlQuery;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#ifndef CONFIGURATIONOPTIONSPARSER_HPP
|
||||
#define CONFIGURATIONOPTIONSPARSER_HPP
|
||||
|
||||
#include <QString>
|
||||
#include <QVariantHash>
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
namespace TINYORM_COMMON_NAMESPACE
|
||||
{
|
||||
|
||||
@@ -646,6 +646,9 @@ namespace Relations {
|
||||
/*! Get the relationships that are touched on save. */
|
||||
inline const QStringList &getTouchedRelations() const
|
||||
{ return model().u_touches; }
|
||||
/*! Determine if the model touches a given relation. */
|
||||
inline bool touches(const QString &relation) const
|
||||
{ return getTouchedRelations().contains(relation); }
|
||||
|
||||
/*! Get all the loaded relations for the instance. */
|
||||
inline const std::unordered_map<QString, RelationsType<AllRelations...>> &
|
||||
@@ -661,7 +664,6 @@ namespace Relations {
|
||||
/*! Unset a loaded relationship. */
|
||||
Model &unsetRelation(const QString &relation);
|
||||
|
||||
|
||||
/* HasTimestamps */
|
||||
/*! Update the model's update timestamp. */
|
||||
bool touch();
|
||||
@@ -3002,9 +3004,9 @@ namespace Relations {
|
||||
const QString &
|
||||
BaseModel<Model, AllRelations...>::getDateFormat() const
|
||||
{
|
||||
return u_dateFormat.isEmpty()
|
||||
return model().u_dateFormat.isEmpty()
|
||||
? getConnection().getQueryGrammar().getDateFormat()
|
||||
: u_dateFormat;
|
||||
: model().u_dateFormat;
|
||||
}
|
||||
|
||||
template<typename Model, typename ...AllRelations>
|
||||
@@ -3200,6 +3202,7 @@ namespace Relations {
|
||||
template<typename Related>
|
||||
QString BaseModel<Model, AllRelations...>::guessBelongsToRelation() const
|
||||
{
|
||||
// TODO reliability, also add Utils::String::studly silverqx
|
||||
auto relation = Utils::Type::classPureBasename<Related>();
|
||||
|
||||
relation[0] = relation[0].toLower();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef HASRELATIONSTORE_H
|
||||
#define HASRELATIONSTORE_H
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "orm/ormtypes.hpp"
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef MODELNOTFOUNDERROR_H
|
||||
#define MODELNOTFOUNDERROR_H
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include "orm/ormruntimeerror.hpp"
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace Orm::Tiny::Relations
|
||||
m_child.setAttribute(m_foreignKey,
|
||||
model.getAttribute(m_ownerKey));
|
||||
|
||||
// CUR docs info in the belongsTo about where the 'relationName' is used silverqx
|
||||
m_child.template setRelation<Related>(m_relationName, model);
|
||||
|
||||
return m_child;
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#ifndef BELONGSTOMANY_H
|
||||
#define BELONGSTOMANY_H
|
||||
|
||||
#include <range/v3/algorithm/copy.hpp>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <range/v3/algorithm/copy_if.hpp>
|
||||
#include <range/v3/iterator/insert_iterators.hpp>
|
||||
#include <range/v3/range/conversion.hpp>
|
||||
#include <range/v3/view/transform.hpp>
|
||||
|
||||
#include "orm/ormdomainerror.hpp"
|
||||
#include "orm/tiny/relations/relation.hpp"
|
||||
#include "orm/utils/attribute.hpp"
|
||||
#include "orm/utils/type.hpp"
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
namespace TINYORM_COMMON_NAMESPACE
|
||||
@@ -104,6 +109,11 @@ namespace Orm::Tiny::Relations
|
||||
const QString &createdAt() const;
|
||||
/*! Get the name of the "updated at" column. */
|
||||
const QString &updatedAt() const;
|
||||
/*! If we're touching the parent model, touch. */
|
||||
void touchIfTouching() const;
|
||||
/*! Touch all of the related models for the relationship.
|
||||
E.g.: Touch all roles associated with this user. */
|
||||
void touch() const override;
|
||||
|
||||
/* InteractsWithPivotTable */
|
||||
/*! Set the columns on the pivot table to retrieve. */
|
||||
@@ -122,6 +132,33 @@ namespace Orm::Tiny::Relations
|
||||
PivotType newPivot(const QVector<AttributeItem> &attributes = {},
|
||||
bool exists = false) const;
|
||||
|
||||
/*! Create a new query builder for the pivot table. */
|
||||
QSharedPointer<QueryBuilder> newPivotQuery() const;
|
||||
/*! Get a new plain query builder for the pivot table. */
|
||||
QSharedPointer<QueryBuilder> newPivotStatement() const;
|
||||
|
||||
/*! Attach models to the parent. */
|
||||
void attach(const QVector<QVariant> &ids,
|
||||
const QVector<AttributeItem> &attributes = {},
|
||||
bool touch = true) const override;
|
||||
/*! Attach models to the parent. */
|
||||
inline void
|
||||
attach(const QVector<std::reference_wrapper<Related>> &models,
|
||||
const QVector<AttributeItem> &attributes = {},
|
||||
bool touch = true) const override;
|
||||
/*! Attach a model to the parent. */
|
||||
inline void
|
||||
attach(const QVariant &id, const QVector<AttributeItem> &attributes = {},
|
||||
bool touch = true) const override;
|
||||
/*! Attach a model to the parent. */
|
||||
inline void
|
||||
attach(const Related &model, const QVector<AttributeItem> &attributes = {},
|
||||
bool touch = true) const override;
|
||||
|
||||
/* Others */
|
||||
/*! Get all of the IDs for the related models. */
|
||||
QVector<QVariant> allRelatedIds() const;
|
||||
|
||||
protected:
|
||||
/*! Set the join clause for the relation query. */
|
||||
const BelongsToMany &performJoin() const;
|
||||
@@ -145,6 +182,30 @@ namespace Orm::Tiny::Relations
|
||||
/*! Get the pivot attributes from a model. */
|
||||
QVector<AttributeItem> migratePivotAttributes(Related &model) const;
|
||||
|
||||
/*! Attach a model to the parent using a custom class. */
|
||||
void attachUsingCustomClass(const QVector<QVariant> &ids,
|
||||
const QVector<AttributeItem> &attributes) const;
|
||||
/*! Create an array of records to insert into the pivot table. */
|
||||
QVector<QVector<AttributeItem>>
|
||||
formatAttachRecords(const QVector<QVariant> &ids,
|
||||
const QVector<AttributeItem> &attributes) const;
|
||||
/*! Create a full attachment record payload. */
|
||||
QVector<AttributeItem>
|
||||
formatAttachRecord(const QVariant &id, const QVector<AttributeItem> &attributes,
|
||||
bool hasTimestamps) const;
|
||||
/*! Create a new pivot attachment record. */
|
||||
QVector<AttributeItem>
|
||||
baseAttachRecord(const QVariant &id, bool timed) const;
|
||||
/*! Set the creation and update timestamps on an attach record. */
|
||||
QVector<AttributeItem> &
|
||||
addTimestampsToAttachment(QVector<AttributeItem> &record,
|
||||
bool exists = false) const;
|
||||
|
||||
/*! Determine if we should touch the parent on sync. */
|
||||
bool touchingParent() const;
|
||||
/*! Attempt to guess the name of the inverse of the relation. */
|
||||
QString guessInverseRelation() const;
|
||||
|
||||
/*! The intermediate table for the relation. */
|
||||
QString m_table;
|
||||
/*! The foreign key of the parent model. */
|
||||
@@ -160,6 +221,7 @@ namespace Orm::Tiny::Relations
|
||||
|
||||
/*! The name of the accessor to use for the "pivot" relationship. */
|
||||
QString m_accessor = QStringLiteral("pivot");
|
||||
// BUG should be QSet, duplicates are not allowed, check all the containers 😭 and use proper containers where I did mistake, from the point of view of duplicates silverqx
|
||||
/*! The pivot table columns to retrieve. */
|
||||
QStringList m_pivotColumns;
|
||||
|
||||
@@ -169,6 +231,17 @@ namespace Orm::Tiny::Relations
|
||||
QString m_pivotCreatedAt;
|
||||
/*! The custom pivot table column for the updated_at timestamp. */
|
||||
QString m_pivotUpdatedAt;
|
||||
|
||||
private:
|
||||
/*! Throw domain exception, when a user tries to override ID key
|
||||
on the pivot table. */
|
||||
void validateAttachAttribute(const AttributeItem &attribute,
|
||||
const QVariant &id) const;
|
||||
/*! Throw domain exception, when a user tries to override ID key
|
||||
on the pivot table. */
|
||||
template<typename KeyType>
|
||||
void throwOverwritingKeyError(const QString &key, const QVariant &original,
|
||||
const QVariant &overwrite) const;
|
||||
};
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
@@ -397,12 +470,49 @@ namespace Orm::Tiny::Relations
|
||||
return m_pivotUpdatedAt;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
void BelongsToMany<Model, Related, PivotType>::touchIfTouching() const
|
||||
{
|
||||
// BUG circular dependency when both models (Model and Related) has set up u_touches silverqx
|
||||
if (touchingParent())
|
||||
this->m_parent.touch();
|
||||
|
||||
if (this->m_parent.touches(m_relationName))
|
||||
touch();
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
void BelongsToMany<Model, Related, PivotType>::touch() const
|
||||
{
|
||||
const auto &related = this->m_related;
|
||||
|
||||
const auto &key = related->getKeyName();
|
||||
|
||||
const QVector<UpdateItem> record {
|
||||
{related->getUpdatedAtColumn(), related->freshTimestampString()},
|
||||
};
|
||||
|
||||
const auto ids = allRelatedIds();
|
||||
|
||||
/* If we actually have IDs for the relation, we will run the query to update all
|
||||
the related model's timestamps, to make sure these all reflect the changes
|
||||
to the parent models. This will help us keep any caching synced up here. */
|
||||
if (!ids.isEmpty())
|
||||
related->newQueryWithoutRelationships()
|
||||
->whereIn(key, ids)
|
||||
.update(record);
|
||||
}
|
||||
|
||||
// TODO perf for all similar methods make rvalue variants, or what if all this methods would be rvalue only, so if it is possible then move and if not then copy silverqx
|
||||
template<class Model, class Related, class PivotType>
|
||||
BelongsToMany<Model, Related, PivotType> &
|
||||
BelongsToMany<Model, Related, PivotType>::withPivot(const QStringList &columns)
|
||||
{
|
||||
ranges::copy(columns, ranges::back_inserter(m_pivotColumns));
|
||||
ranges::copy_if(columns, ranges::back_inserter(m_pivotColumns),
|
||||
[this](const auto &column)
|
||||
{
|
||||
return !m_pivotColumns.contains(column);
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -424,6 +534,102 @@ namespace Orm::Tiny::Relations
|
||||
.setPivotKeys(m_foreignPivotKey, m_relatedPivotKey);
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
QSharedPointer<QueryBuilder>
|
||||
BelongsToMany<Model, Related, PivotType>::newPivotQuery() const
|
||||
{
|
||||
// Ownership of the QSharedPointer<QueryBuilder>
|
||||
auto query = newPivotStatement();
|
||||
|
||||
// TODO relations, add support for BelongsToMany::where/whereIn/whereNull silverqx
|
||||
// for (auto &[column, value, comparison, condition] : m_pivotWheres)
|
||||
// query->where(column, value, comparison, condition);
|
||||
|
||||
// for (auto &[column, values, condition, nope] : m_pivotWhereIns)
|
||||
// query->whereIn(column, values, condition, nope);
|
||||
|
||||
// for (auto &[😭, condition, nope] : m_pivotWhereNulls)
|
||||
// query->whereNull(columns, condition, nope);
|
||||
|
||||
query->whereEq(m_foreignPivotKey, this->m_parent[m_parentKey]);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
QSharedPointer<QueryBuilder>
|
||||
BelongsToMany<Model, Related, PivotType>::newPivotStatement() const
|
||||
{
|
||||
auto query = this->m_query->getQuery().newQuery();
|
||||
|
||||
query->from(m_table);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
void BelongsToMany<Model, Related, PivotType>::attach(
|
||||
const QVector<QVariant> &ids, const QVector<AttributeItem> &attributes,
|
||||
const bool touch) const
|
||||
{
|
||||
if constexpr (std::is_same_v<PivotType, Pivot>)
|
||||
/* Here we will insert the attachment records into the pivot table. Once
|
||||
we have inserted the records, we will touch the relationships if
|
||||
necessary and the function will return. */
|
||||
newPivotStatement()->insert(Utils::Attribute::convertVectorsToMaps(
|
||||
formatAttachRecords(ids, attributes)));
|
||||
else
|
||||
attachUsingCustomClass(ids, attributes);
|
||||
|
||||
if (touch)
|
||||
touchIfTouching();
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
void BelongsToMany<Model, Related, PivotType>::attach(
|
||||
const QVector<std::reference_wrapper<Related>> &models,
|
||||
const QVector<AttributeItem> &attributes, const bool touch) const
|
||||
{
|
||||
QVector<QVariant> ids;
|
||||
ids.reserve(models.size());
|
||||
|
||||
for (const auto &model : models)
|
||||
ids << model.get().getAttribute(m_relatedKey);
|
||||
|
||||
attach(ids, attributes, touch);
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
void BelongsToMany<Model, Related, PivotType>::attach(
|
||||
const QVariant &id, const QVector<AttributeItem> &attributes,
|
||||
const bool touch) const
|
||||
{
|
||||
attach(QVector {id}, attributes, touch);
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
void BelongsToMany<Model, Related, PivotType>::attach(
|
||||
const Related &model, const QVector<AttributeItem> &attributes,
|
||||
const bool touch) const
|
||||
{
|
||||
attach(QVector {model.getAttribute(m_relatedKey)}, attributes, touch);
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
QVector<QVariant>
|
||||
BelongsToMany<Model, Related, PivotType>::allRelatedIds() const
|
||||
{
|
||||
QVector<QVariant> ids;
|
||||
|
||||
// Ownership of the QSharedPointer<QueryBuilder>
|
||||
auto [ok, query] = newPivotQuery()->get({m_relatedPivotKey});
|
||||
|
||||
while (query.next())
|
||||
ids << query.value(m_relatedPivotKey);
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
const BelongsToMany<Model, Related, PivotType> &
|
||||
BelongsToMany<Model, Related, PivotType>::performJoin() const
|
||||
@@ -525,6 +731,143 @@ namespace Orm::Tiny::Relations
|
||||
return values;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
void BelongsToMany<Model, Related, PivotType>::attachUsingCustomClass(
|
||||
const QVector<QVariant> &ids,
|
||||
const QVector<AttributeItem> &attributes) const
|
||||
{
|
||||
for (const auto &record : formatAttachRecords(ids, attributes))
|
||||
newPivot(record).save();
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
QVector<QVector<AttributeItem>>
|
||||
BelongsToMany<Model, Related, PivotType>::formatAttachRecords(
|
||||
const QVector<QVariant> &ids,
|
||||
const QVector<AttributeItem> &attributes) const
|
||||
{
|
||||
QVector<QVector<AttributeItem>> records;
|
||||
|
||||
auto hasTimestamps = hasPivotColumn(createdAt()) ||
|
||||
hasPivotColumn(updatedAt());
|
||||
|
||||
/* To create the attachment records, we will simply spin through the IDs given
|
||||
and create a new record to insert for each ID with extra attributes to be
|
||||
placed in other columns. */
|
||||
for (const auto &id : ids)
|
||||
records << formatAttachRecord(id, attributes, hasTimestamps);
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
QVector<AttributeItem>
|
||||
BelongsToMany<Model, Related, PivotType>::formatAttachRecord(
|
||||
const QVariant &id, const QVector<AttributeItem> &attributes,
|
||||
const bool hasTimestamps) const
|
||||
{
|
||||
auto baseAttributes = baseAttachRecord(id, hasTimestamps);
|
||||
|
||||
for (const auto &attribute : attributes) {
|
||||
// NOTE api different silverqx
|
||||
validateAttachAttribute(attribute, id);
|
||||
|
||||
baseAttributes << attribute;
|
||||
}
|
||||
|
||||
return baseAttributes;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
QVector<AttributeItem>
|
||||
BelongsToMany<Model, Related, PivotType>::baseAttachRecord(
|
||||
const QVariant &id, const bool timed) const
|
||||
{
|
||||
QVector<AttributeItem> record;
|
||||
|
||||
record.append({m_relatedPivotKey, id});
|
||||
record.append({m_foreignPivotKey, this->m_parent[m_parentKey]});
|
||||
|
||||
/* If the record needs to have creation and update timestamps, we will make
|
||||
them by calling the parent model's "freshTimestamp" method, which will
|
||||
provide us with a fresh timestamp in this model's preferred format. */
|
||||
if (timed)
|
||||
addTimestampsToAttachment(record);
|
||||
|
||||
// TODO pivot, withPivotValues silverqx
|
||||
// for (auto &[column, value] as m_pivotValues)
|
||||
// record.append(column, value);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
QVector<AttributeItem> &
|
||||
BelongsToMany<Model, Related, PivotType>::addTimestampsToAttachment(
|
||||
QVector<AttributeItem> &record, const bool exists) const
|
||||
{
|
||||
QVariant fresh = this->m_parent.freshTimestamp();
|
||||
|
||||
// If custom pivot is used
|
||||
if constexpr (!std::is_same_v<PivotType, Pivot>)
|
||||
fresh = fresh.toDateTime().toString(PivotType().getDateFormat());
|
||||
|
||||
if (!exists && hasPivotColumn(createdAt()))
|
||||
record.append({createdAt(), fresh});
|
||||
|
||||
if (hasPivotColumn(updatedAt()))
|
||||
record.append({updatedAt(), fresh});
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
bool BelongsToMany<Model, Related, PivotType>::touchingParent() const
|
||||
{
|
||||
return this->m_related->touches(guessInverseRelation());
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
QString BelongsToMany<Model, Related, PivotType>::guessInverseRelation() const
|
||||
{
|
||||
// TODO relations, add parent touches (eg parentTouchesName) to the BaseModel::belongsToMany factory method silverqx
|
||||
auto relation = Utils::Type::classPureBasename<Model>();
|
||||
|
||||
relation[0] = relation[0].toLower();
|
||||
|
||||
return relation + QChar('s');
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
void BelongsToMany<Model, Related, PivotType>::validateAttachAttribute(
|
||||
const AttributeItem &attribute, const QVariant &id) const
|
||||
{
|
||||
// Don't overwrite ID keys, throw domain exception
|
||||
if (attribute.key == m_foreignPivotKey)
|
||||
throwOverwritingKeyError<Model::KeyType>(attribute.key,
|
||||
this->m_parent[m_parentKey],
|
||||
attribute.value);
|
||||
else if (attribute.key == m_relatedPivotKey)
|
||||
throwOverwritingKeyError<Related::KeyType>(attribute.key, id,
|
||||
attribute.value);
|
||||
}
|
||||
|
||||
template<class Model, class Related, class PivotType>
|
||||
template<typename KeyType>
|
||||
void BelongsToMany<Model, Related, PivotType>::throwOverwritingKeyError(
|
||||
const QString &key, const QVariant &original,
|
||||
const QVariant &overwrite) const
|
||||
{
|
||||
static const auto overwriteMessage =
|
||||
QStringLiteral("You can not overwrite '%1' ID key; "
|
||||
"original value : %2, your value : %3.");
|
||||
|
||||
throw OrmDomainError(overwriteMessage.arg(
|
||||
qualifyPivotColumn(key),
|
||||
QString::number(original.value<KeyType>()),
|
||||
QString::number(overwrite.value<KeyType>())));
|
||||
}
|
||||
|
||||
} // namespace Orm::Tiny::Relations
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
} // namespace TINYORM_COMMON_NAMESPACE
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <QtSql/QSqlQuery>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <range/v3/action/sort.hpp>
|
||||
#include <range/v3/action/unique.hpp>
|
||||
|
||||
@@ -95,7 +97,7 @@ namespace Relations
|
||||
|
||||
/* Others */
|
||||
/*! Touch all of the related models for the relationship. */
|
||||
void touch() const;
|
||||
virtual void touch() const;
|
||||
/*! Run a raw update against the base query. */
|
||||
std::tuple<int, QSqlQuery>
|
||||
rawUpdate(const QVector<UpdateItem> &values = {}) const;
|
||||
@@ -367,6 +369,7 @@ namespace Relations
|
||||
Builder<Related> &whereKeyNot(const QVariant &id) const;
|
||||
|
||||
/* Inserting operations on the relationship */
|
||||
// TODO check, would be possible to disable this by SFINAE by current class type? eg std::enable_if this is ManyRelation or PivotRelation; and if yes, then it's a good idea do it this way? silverqx
|
||||
/*! Attach a model instance to the parent model. */
|
||||
inline virtual std::tuple<bool, Related &> save(Related &) const
|
||||
{ throw OrmLogicError("The 'save' method is not implemented for this "
|
||||
@@ -412,6 +415,32 @@ namespace Relations
|
||||
inline virtual Model &disassociate() const
|
||||
{ return dissociate(); }
|
||||
|
||||
/* Many-To-Many */
|
||||
/*! Attach models to the parent. */
|
||||
virtual void attach(const QVector<QVariant> &,
|
||||
const QVector<AttributeItem> & = {},
|
||||
bool = true) const
|
||||
{ throw OrmLogicError("The 'attach' method is not implemented for this "
|
||||
"relation type."); }
|
||||
/*! Attach models to the parent. */
|
||||
virtual void attach(const QVector<std::reference_wrapper<Related>> &,
|
||||
const QVector<AttributeItem> & = {},
|
||||
bool = true) const
|
||||
{ throw OrmLogicError("The 'attach' method is not implemented for this "
|
||||
"relation type."); }
|
||||
/*! Attach a model to the parent. */
|
||||
virtual void attach(const QVariant &,
|
||||
const QVector<AttributeItem> & = {},
|
||||
bool = true) const
|
||||
{ throw OrmLogicError("The 'attach' method is not implemented for this "
|
||||
"relation type."); }
|
||||
/*! Attach a model to the parent. */
|
||||
virtual void attach(const Related &,
|
||||
const QVector<AttributeItem> & = {},
|
||||
bool = true) const
|
||||
{ throw OrmLogicError("The 'attach' method is not implemented for this "
|
||||
"relation type."); }
|
||||
|
||||
protected:
|
||||
/*! Initialize a Relation instance. */
|
||||
inline void init() const
|
||||
|
||||
@@ -10,10 +10,14 @@ namespace TINYORM_COMMON_NAMESPACE
|
||||
#endif
|
||||
namespace Orm::Utils::Attribute
|
||||
{
|
||||
/*! Convert a AttributeItem QVector to the QVariantMap. */
|
||||
/*! Convert a AttributeItem QVector to QVariantMap. */
|
||||
SHAREDLIB_EXPORT QVariantMap
|
||||
convertVectorToMap(const QVector<AttributeItem> &attributes);
|
||||
/*! Convert a AttributeItem QVector to the UpdateItem QVector. */
|
||||
/*! Convert a vector of AttributeItem QVectors to the vector of QVariantMaps. */
|
||||
SHAREDLIB_EXPORT QVector<QVariantMap>
|
||||
convertVectorsToMaps(const QVector<QVector<AttributeItem>> &attributesVector);
|
||||
|
||||
/*! Convert a AttributeItem QVector to UpdateItem QVector. */
|
||||
SHAREDLIB_EXPORT QVector<UpdateItem>
|
||||
convertVectorToUpdateItem(const QVector<AttributeItem> &attributes);
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef UTILS_STRING_H
|
||||
#define UTILS_STRING_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "export.hpp"
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#if defined __cplusplus
|
||||
/* Add C++ includes here */
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "orm/concerns/detectslostconnections.hpp"
|
||||
|
||||
#include <QVector>
|
||||
|
||||
#include "orm/sqlerror.hpp"
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <QtSql/QSqlQuery>
|
||||
#include <QVersionNumber>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
namespace TINYORM_COMMON_NAMESPACE
|
||||
{
|
||||
|
||||
@@ -70,10 +70,9 @@ bool DatabaseConnection::beginTransaction()
|
||||
|
||||
static const auto query = QStringLiteral("START TRANSACTION");
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
#endif
|
||||
if (m_debugSql || m_countingElapsed)
|
||||
timer.start();
|
||||
|
||||
if (!getQtConnection().transaction())
|
||||
throw SqlTransactionError(
|
||||
@@ -82,12 +81,15 @@ bool DatabaseConnection::beginTransaction()
|
||||
|
||||
m_inTransaction = true;
|
||||
|
||||
// Queries execution time counter / Query statements counter
|
||||
const auto elapsed = hitTransactionalCounters(timer);
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
// TODO multidriver, when will be added support for more drivers, than the Grammar will have to compile this silverqx
|
||||
/* 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. */
|
||||
logTransactionQuery(query, timer.elapsed());
|
||||
logTransactionQuery(query, elapsed);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -99,10 +101,9 @@ bool DatabaseConnection::commit()
|
||||
|
||||
static const auto query = QStringLiteral("COMMIT");
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
#endif
|
||||
if (m_debugSql || m_countingElapsed)
|
||||
timer.start();
|
||||
|
||||
if (!getQtConnection().commit())
|
||||
throw SqlTransactionError(
|
||||
@@ -111,11 +112,14 @@ bool DatabaseConnection::commit()
|
||||
|
||||
m_inTransaction = false;
|
||||
|
||||
// Queries execution time counter / Query statements counter
|
||||
const auto elapsed = hitTransactionalCounters(timer);
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
/* 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. */
|
||||
logTransactionQuery(query, timer.elapsed());
|
||||
logTransactionQuery(query, elapsed);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -127,10 +131,9 @@ bool DatabaseConnection::rollBack()
|
||||
|
||||
static const auto query = QStringLiteral("ROLLBACK");
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
#endif
|
||||
if (m_debugSql || m_countingElapsed)
|
||||
timer.start();
|
||||
|
||||
if (!getQtConnection().rollback())
|
||||
throw SqlTransactionError(
|
||||
@@ -139,11 +142,14 @@ bool DatabaseConnection::rollBack()
|
||||
|
||||
m_inTransaction = false;
|
||||
|
||||
// Queries execution time counter / Query statements counter
|
||||
const auto elapsed = hitTransactionalCounters(timer);
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
/* 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. */
|
||||
logTransactionQuery(query, timer.elapsed());
|
||||
logTransactionQuery(query, elapsed);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -157,10 +163,9 @@ bool DatabaseConnection::savepoint(const QString &id)
|
||||
auto savePoint = getQtQuery();
|
||||
const auto query = QStringLiteral("SAVEPOINT %1_%2").arg(SAVEPOINT_NAMESPACE, id);
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
#endif
|
||||
if (m_debugSql || m_countingElapsed)
|
||||
timer.start();
|
||||
|
||||
// Execute a savepoint query
|
||||
if (!savePoint.exec(query))
|
||||
@@ -171,11 +176,14 @@ bool DatabaseConnection::savepoint(const QString &id)
|
||||
|
||||
++m_savepoints;
|
||||
|
||||
// Queries execution time counter / Query statements counter
|
||||
const auto elapsed = hitTransactionalCounters(timer);
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
/* 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. */
|
||||
logTransactionQuery(query, timer.elapsed());
|
||||
logTransactionQuery(query, elapsed);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -195,10 +203,9 @@ bool DatabaseConnection::rollbackToSavepoint(const QString &id)
|
||||
const auto query = QStringLiteral("ROLLBACK TO SAVEPOINT %1_%2")
|
||||
.arg(SAVEPOINT_NAMESPACE, id);
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
#endif
|
||||
if (m_debugSql || m_countingElapsed)
|
||||
timer.start();
|
||||
|
||||
// Execute a rollback to savepoint query
|
||||
if (!rollbackToSavepoint.exec(query))
|
||||
@@ -209,11 +216,14 @@ bool DatabaseConnection::rollbackToSavepoint(const QString &id)
|
||||
|
||||
m_savepoints = std::max<int>(0, m_savepoints - 1);
|
||||
|
||||
// Queries execution time counter / Query statements counter
|
||||
const auto elapsed = hitTransactionalCounters(timer);
|
||||
|
||||
#ifdef TINYORM_DEBUG_SQL
|
||||
/* 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. */
|
||||
logTransactionQuery(query, timer.elapsed());
|
||||
logTransactionQuery(query, elapsed);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -285,8 +295,13 @@ DatabaseConnection::statement(const QString &queryString,
|
||||
|
||||
bindValues(query, prepareBindings(bindings));
|
||||
|
||||
if (const auto ok = query.exec(); ok)
|
||||
if (const auto ok = query.exec(); ok) {
|
||||
// Query statements counter
|
||||
if (m_countingStatements)
|
||||
++m_statementsCounter.normal;
|
||||
|
||||
return {ok, query};
|
||||
}
|
||||
|
||||
/* If an error occurs when attempting to run a query, we'll transform it
|
||||
to the exception QueryError(), which formats the error message to
|
||||
@@ -313,8 +328,13 @@ DatabaseConnection::affectingStatement(const QString &queryString,
|
||||
|
||||
bindValues(query, prepareBindings(bindings));
|
||||
|
||||
if (query.exec())
|
||||
if (query.exec()) {
|
||||
// Affecting statements counter
|
||||
if (m_countingStatements)
|
||||
++m_statementsCounter.affecting;
|
||||
|
||||
return {query.numRowsAffected(), query};
|
||||
}
|
||||
|
||||
/* If an error occurs when attempting to run a query, we'll transform it
|
||||
to the exception QueryError(), which formats the error message to
|
||||
@@ -402,8 +422,9 @@ void DatabaseConnection::bindValues(QSqlQuery &query,
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseConnection::logQuery(const QSqlQuery &query,
|
||||
const std::optional<quint64> elapsed = std::nullopt) const
|
||||
void DatabaseConnection::logQuery(
|
||||
const QSqlQuery &query,
|
||||
const std::optional<quint64> &elapsed = std::nullopt)
|
||||
{
|
||||
qDebug().nospace().noquote()
|
||||
<< "Executed prepared query (" << (elapsed ? *elapsed : -1) << "ms, "
|
||||
@@ -413,7 +434,7 @@ void DatabaseConnection::logQuery(const QSqlQuery &query,
|
||||
|
||||
void DatabaseConnection::logTransactionQuery(
|
||||
const QString &query,
|
||||
const std::optional<quint64> elapsed = std::nullopt) const
|
||||
const std::optional<quint64> elapsed = std::nullopt)
|
||||
{
|
||||
// This is only internal method and logs the passed string
|
||||
qDebug().nospace().noquote()
|
||||
@@ -421,6 +442,25 @@ void DatabaseConnection::logTransactionQuery(
|
||||
<< query;
|
||||
}
|
||||
|
||||
std::optional<quint64>
|
||||
DatabaseConnection::hitTransactionalCounters(const QElapsedTimer &timer)
|
||||
{
|
||||
/* This function was extracted to prevent duplicit code only. */
|
||||
std::optional<quint64> elapsed;
|
||||
|
||||
if (m_debugSql || m_countingElapsed)
|
||||
elapsed = timer.elapsed();
|
||||
|
||||
// Queries execution time counter
|
||||
if (m_countingElapsed)
|
||||
m_elapsedCounter += *elapsed;
|
||||
// Query statements counter
|
||||
if (m_countingStatements)
|
||||
++m_statementsCounter.transactional;
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
bool DatabaseConnection::pingDatabase()
|
||||
{
|
||||
auto qtConnection = getQtConnection();
|
||||
@@ -519,6 +559,106 @@ QVariant DatabaseConnection::getConfig() const
|
||||
return m_config;
|
||||
}
|
||||
|
||||
bool DatabaseConnection::countingElapsed() const
|
||||
{
|
||||
return m_countingElapsed;
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseConnection::enableElapsedCounter()
|
||||
{
|
||||
m_countingElapsed = true;
|
||||
m_elapsedCounter = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseConnection::disableElapsedCounter()
|
||||
{
|
||||
m_countingElapsed = false;
|
||||
m_elapsedCounter = -1;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
qint64 DatabaseConnection::getElapsedCounter() const
|
||||
{
|
||||
return m_elapsedCounter;
|
||||
}
|
||||
|
||||
qint64 DatabaseConnection::takeElapsedCounter()
|
||||
{
|
||||
if (!m_countingElapsed)
|
||||
return -1;
|
||||
|
||||
const auto elapsed = m_elapsedCounter;
|
||||
|
||||
m_elapsedCounter = 0;
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseConnection::resetElapsedCounter()
|
||||
{
|
||||
m_elapsedCounter = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DatabaseConnection::countingStatements() const
|
||||
{
|
||||
return m_countingStatements;
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseConnection::enableStatementsCounter()
|
||||
{
|
||||
m_countingStatements = true;
|
||||
|
||||
m_statementsCounter.normal = 0;
|
||||
m_statementsCounter.affecting = 0;
|
||||
m_statementsCounter.transactional = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseConnection::disableStatementsCounter()
|
||||
{
|
||||
m_countingStatements = false;
|
||||
|
||||
m_statementsCounter.normal = -1;
|
||||
m_statementsCounter.affecting = -1;
|
||||
m_statementsCounter.transactional = -1;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const StatementsCounter &DatabaseConnection::getStatementsCounter() const
|
||||
{
|
||||
return m_statementsCounter;
|
||||
}
|
||||
|
||||
StatementsCounter DatabaseConnection::takeStatementsCounter()
|
||||
{
|
||||
if (!m_countingStatements)
|
||||
return {};
|
||||
|
||||
const auto counter = m_statementsCounter;
|
||||
|
||||
m_statementsCounter.normal = 0;
|
||||
m_statementsCounter.affecting = 0;
|
||||
m_statementsCounter.transactional = 0;
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseConnection::resetStatementsCounter()
|
||||
{
|
||||
m_statementsCounter.normal = 0;
|
||||
m_statementsCounter.affecting = 0;
|
||||
m_statementsCounter.transactional = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DatabaseConnection::reconnectIfMissingConnection() const
|
||||
{
|
||||
if (!m_qtConnectionResolver) {
|
||||
|
||||
@@ -57,6 +57,7 @@ DatabaseManager &DatabaseManager::setupDefaultReconnector()
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO add 'createLazy = true' paramater, add support to create eager connection silverqx
|
||||
std::unique_ptr<DatabaseManager>
|
||||
DatabaseManager::create(const QVariantHash &config, const QString &connection)
|
||||
{
|
||||
@@ -206,7 +207,7 @@ ConnectionInterface &DatabaseManager::reconnect(QString name)
|
||||
if (!m_connections.contains(name))
|
||||
return connection(name);
|
||||
|
||||
return refreshPdoConnections(name);
|
||||
return refreshQtConnections(name);
|
||||
}
|
||||
|
||||
void DatabaseManager::disconnect(QString name) const
|
||||
@@ -218,7 +219,7 @@ void DatabaseManager::disconnect(QString name) const
|
||||
m_connections.find(name)->second->disconnect();
|
||||
}
|
||||
|
||||
const QStringList DatabaseManager::supportedDrivers() const
|
||||
QStringList DatabaseManager::supportedDrivers() const
|
||||
{
|
||||
// TODO future add method to not only supported drivers, but also check if driver is available/loadable by qsqldatabase silverqx
|
||||
// aaaaaaaaaaaaaachjo 🤔😁
|
||||
@@ -226,6 +227,18 @@ const QStringList DatabaseManager::supportedDrivers() const
|
||||
// return {"mysql", "pgsql", "sqlite", "sqlsrv"};
|
||||
}
|
||||
|
||||
QStringList DatabaseManager::connectionNames() const
|
||||
{
|
||||
QStringList names;
|
||||
// TODO overflow, add check code https://stackoverflow.com/questions/22184403/how-to-cast-the-size-t-to-double-or-int-c/22184657#22184657 silverqx
|
||||
names.reserve(static_cast<int>(m_connections.size()));
|
||||
|
||||
for (auto &connection : m_connections)
|
||||
names << connection.first;
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
const QString &
|
||||
DatabaseManager::getDefaultConnection() const
|
||||
{
|
||||
@@ -245,6 +258,230 @@ DatabaseManager::setReconnector(const ReconnectorType &reconnector)
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DatabaseManager::countingElapsed(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).countingElapsed();
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseManager::enableElapsedCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).enableElapsedCounter();
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseManager::disableElapsedCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).disableElapsedCounter();
|
||||
}
|
||||
|
||||
qint64 DatabaseManager::getElapsedCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).getElapsedCounter();
|
||||
}
|
||||
|
||||
qint64 DatabaseManager::takeElapsedCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).takeElapsedCounter();
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseManager::resetElapsedCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).resetElapsedCounter();
|
||||
}
|
||||
|
||||
bool DatabaseManager::anyCountingElapsed()
|
||||
{
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections)
|
||||
if (connection(connectionName).countingElapsed())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DatabaseManager::enableAllElapsedCounters()
|
||||
{
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections)
|
||||
connection(connectionName).enableElapsedCounter();
|
||||
}
|
||||
|
||||
void DatabaseManager::disableAllElapsedCounters()
|
||||
{
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections)
|
||||
connection(connectionName).disableElapsedCounter();
|
||||
}
|
||||
|
||||
qint64 DatabaseManager::getAllElapsedCounters()
|
||||
{
|
||||
if (!anyCountingElapsed())
|
||||
return -1;
|
||||
|
||||
qint64 elapsed = 0;
|
||||
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections) {
|
||||
const auto &connection = this->connection(connectionName);
|
||||
|
||||
if (connection.countingElapsed())
|
||||
elapsed += connection.getElapsedCounter();
|
||||
}
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
qint64 DatabaseManager::takeAllElapsedCounters()
|
||||
{
|
||||
if (!anyCountingElapsed())
|
||||
return -1;
|
||||
|
||||
qint64 elapsed = 0;
|
||||
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections) {
|
||||
auto &connection = this->connection(connectionName);
|
||||
|
||||
if (connection.countingElapsed())
|
||||
elapsed += connection.takeElapsedCounter();
|
||||
}
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
void DatabaseManager::resetAllElapsedCounters()
|
||||
{
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections) {
|
||||
auto &connection = this->connection(connectionName);
|
||||
|
||||
if (connection.countingElapsed())
|
||||
connection.resetElapsedCounter();
|
||||
}
|
||||
}
|
||||
|
||||
bool DatabaseManager::countingStatements(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).countingStatements();
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseManager::enableStatementsCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).enableStatementsCounter();
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseManager::disableStatementsCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).disableStatementsCounter();
|
||||
}
|
||||
|
||||
const StatementsCounter &DatabaseManager::getStatementsCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).getStatementsCounter();
|
||||
}
|
||||
|
||||
StatementsCounter DatabaseManager::takeStatementsCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).takeStatementsCounter();
|
||||
}
|
||||
|
||||
DatabaseConnection &DatabaseManager::resetStatementsCounter(const QString &connection)
|
||||
{
|
||||
return this->connection(connection).resetStatementsCounter();
|
||||
}
|
||||
|
||||
bool DatabaseManager::anyCountingStatements()
|
||||
{
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections)
|
||||
if (connection(connectionName).countingStatements())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DatabaseManager::enableAllStatementCounters()
|
||||
{
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections)
|
||||
connection(connectionName).enableStatementsCounter();
|
||||
}
|
||||
|
||||
void DatabaseManager::disableAllStatementCounters()
|
||||
{
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections)
|
||||
connection(connectionName).disableStatementsCounter();
|
||||
}
|
||||
|
||||
StatementsCounter DatabaseManager::getAllStatementCounters()
|
||||
{
|
||||
StatementsCounter counter;
|
||||
|
||||
if (!anyCountingStatements())
|
||||
return counter;
|
||||
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections) {
|
||||
const auto &connection = this->connection(connectionName);
|
||||
|
||||
if (connection.countingStatements()) {
|
||||
const auto &counter_ = connection.getStatementsCounter();
|
||||
|
||||
counter.normal += counter_.normal;
|
||||
counter.affecting += counter_.affecting;
|
||||
counter.transactional += counter_.transactional;
|
||||
}
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
StatementsCounter DatabaseManager::takeAllStatementCounters()
|
||||
{
|
||||
StatementsCounter counter;
|
||||
|
||||
if (!anyCountingStatements())
|
||||
return counter;
|
||||
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections) {
|
||||
auto &connection = this->connection(connectionName);
|
||||
|
||||
if (connection.countingElapsed()) {
|
||||
const auto counter_ = connection.takeStatementsCounter();
|
||||
|
||||
counter.normal += counter_.normal;
|
||||
counter.affecting += counter_.affecting;
|
||||
counter.transactional += counter_.transactional;
|
||||
}
|
||||
}
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
void DatabaseManager::resetAllStatementCounters()
|
||||
{
|
||||
const auto connections = connectionNames();
|
||||
|
||||
for (const auto &connectionName : connections) {
|
||||
auto &connection = this->connection(connectionName);
|
||||
|
||||
if (connection.countingElapsed())
|
||||
connection.resetStatementsCounter();
|
||||
}
|
||||
}
|
||||
|
||||
const QString &
|
||||
DatabaseManager::parseConnectionName(const QString &name) const
|
||||
{
|
||||
@@ -294,7 +531,7 @@ DatabaseManager::configure(std::unique_ptr<DatabaseConnection> connection) const
|
||||
}
|
||||
|
||||
DatabaseConnection &
|
||||
DatabaseManager::refreshPdoConnections(const QString &name)
|
||||
DatabaseManager::refreshQtConnections(const QString &name)
|
||||
{
|
||||
/* Make OUR new connection and copy the connection resolver from this new
|
||||
connection to the current connection, this ensure that the connection
|
||||
|
||||
127
src/orm/db.cpp
127
src/orm/db.cpp
@@ -1,5 +1,7 @@
|
||||
#include "orm/db.hpp"
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
namespace TINYORM_COMMON_NAMESPACE
|
||||
{
|
||||
@@ -55,6 +57,11 @@ const QStringList DB::supportedDrivers()
|
||||
return manager().supportedDrivers();
|
||||
}
|
||||
|
||||
QStringList DB::connectionNames()
|
||||
{
|
||||
return manager().connectionNames();
|
||||
}
|
||||
|
||||
const QString &DB::getDefaultConnection()
|
||||
{
|
||||
return manager().getDefaultConnection();
|
||||
@@ -165,6 +172,126 @@ uint DB::transactionLevel(const QString &connection)
|
||||
return manager().connection(connection).transactionLevel();
|
||||
}
|
||||
|
||||
bool DB::countingElapsed(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).countingElapsed();
|
||||
}
|
||||
|
||||
DatabaseConnection &DB::enableElapsedCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).enableElapsedCounter();
|
||||
}
|
||||
|
||||
DatabaseConnection &DB::disableElapsedCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).disableElapsedCounter();
|
||||
}
|
||||
|
||||
qint64 DB::getElapsedCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).getElapsedCounter();
|
||||
}
|
||||
|
||||
qint64 DB::takeElapsedCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).takeElapsedCounter();
|
||||
}
|
||||
|
||||
DatabaseConnection &DB::resetElapsedCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).resetElapsedCounter();
|
||||
}
|
||||
|
||||
bool DB::anyCountingElapsed()
|
||||
{
|
||||
return manager().anyCountingElapsed();
|
||||
}
|
||||
|
||||
void DB::enableAllElapsedCounters()
|
||||
{
|
||||
return manager().enableAllElapsedCounters();
|
||||
}
|
||||
|
||||
void DB::disableAllElapsedCounters()
|
||||
{
|
||||
return manager().disableAllElapsedCounters();
|
||||
}
|
||||
|
||||
qint64 DB::getAllElapsedCounters()
|
||||
{
|
||||
return manager().getAllElapsedCounters();
|
||||
}
|
||||
|
||||
qint64 DB::takeAllElapsedCounters()
|
||||
{
|
||||
return manager().takeAllElapsedCounters();
|
||||
}
|
||||
|
||||
void DB::resetAllElapsedCounters()
|
||||
{
|
||||
return manager().resetAllElapsedCounters();
|
||||
}
|
||||
|
||||
bool DB::countingStatements(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).countingStatements();
|
||||
}
|
||||
|
||||
DatabaseConnection &DB::enableStatementsCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).enableStatementsCounter();
|
||||
}
|
||||
|
||||
DatabaseConnection &DB::disableStatementsCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).disableStatementsCounter();
|
||||
}
|
||||
|
||||
const StatementsCounter &DB::getStatementsCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).getStatementsCounter();
|
||||
}
|
||||
|
||||
StatementsCounter DB::takeStatementsCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).takeStatementsCounter();
|
||||
}
|
||||
|
||||
DatabaseConnection &DB::resetStatementsCounter(const QString &connection)
|
||||
{
|
||||
return manager().connection(connection).resetStatementsCounter();
|
||||
}
|
||||
|
||||
bool DB::anyCountingStatements()
|
||||
{
|
||||
return manager().anyCountingStatements();
|
||||
}
|
||||
|
||||
void DB::enableAllStatementCounters()
|
||||
{
|
||||
return manager().enableAllStatementCounters();
|
||||
}
|
||||
|
||||
void DB::disableAllStatementCounters()
|
||||
{
|
||||
return manager().disableAllStatementCounters();
|
||||
}
|
||||
|
||||
StatementsCounter DB::getAllStatementCounters()
|
||||
{
|
||||
return manager().getAllStatementCounters();
|
||||
}
|
||||
|
||||
StatementsCounter DB::takeAllStatementCounters()
|
||||
{
|
||||
return manager().takeAllStatementCounters();
|
||||
}
|
||||
|
||||
void DB::resetAllStatementCounters()
|
||||
{
|
||||
return manager().resetAllStatementCounters();
|
||||
}
|
||||
|
||||
} // namespace Orm
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
} // namespace TINYORM_COMMON_NAMESPACE
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "orm/query/querybuilder.hpp"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <range/v3/algorithm/copy.hpp>
|
||||
#include <range/v3/iterator/insert_iterators.hpp>
|
||||
|
||||
@@ -612,7 +614,7 @@ QString Builder::getFromWithoutAlias() const
|
||||
QRegularExpression::CaseInsensitiveOption)).first();
|
||||
}
|
||||
|
||||
// TODO next revisit QSharedPointer silverqx
|
||||
// TODO next revisit QSharedPointer, after few weeks I'm pretty sure that this can/should be std::unique_pre, like in the TinyBuilder, I need to check if more instances need to save this pointer at once, if don't then I have to change it silverqx
|
||||
QSharedPointer<Builder> Builder::newQuery() const
|
||||
{
|
||||
return QSharedPointer<Builder>::create(m_connection, m_grammar);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "orm/support/configurationoptionsparser.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "orm/connectors/connector.hpp"
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
|
||||
@@ -11,21 +11,36 @@ QVariantMap
|
||||
convertVectorToMap(const QVector<AttributeItem> &attributes)
|
||||
{
|
||||
// TODO mistake m_attributes/m_original 😭 silverqx
|
||||
QVariantMap values;
|
||||
for (const auto &attribute : attributes)
|
||||
values.insert(attribute.key, attribute.value);
|
||||
QVariantMap result;
|
||||
|
||||
return values;
|
||||
for (const auto &attribute : attributes)
|
||||
result.insert(attribute.key, attribute.value);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QVector<QVariantMap>
|
||||
convertVectorsToMaps(const QVector<QVector<AttributeItem>> &attributesVector)
|
||||
{
|
||||
const auto size = attributesVector.size();
|
||||
QVector<QVariantMap> result(size);
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
for (const auto &attribute : attributesVector[i])
|
||||
result[i][attribute.key] = attribute.value;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QVector<UpdateItem>
|
||||
convertVectorToUpdateItem(const QVector<AttributeItem> &attributes)
|
||||
{
|
||||
QVector<UpdateItem> values;
|
||||
for (const auto &attribute : attributes)
|
||||
values.append({attribute.key, attribute.value});
|
||||
QVector<UpdateItem> result;
|
||||
|
||||
return values;
|
||||
for (const auto &attribute : attributes)
|
||||
result.append({attribute.key, attribute.value});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Orm
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "orm/utils/string.hpp"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#ifdef TINYORM_COMMON_NAMESPACE
|
||||
namespace TINYORM_COMMON_NAMESPACE
|
||||
{
|
||||
|
||||
@@ -71,6 +71,9 @@ private:
|
||||
/*! The connection name for the model. */
|
||||
QString u_connection {"tinyorm_mysql_tests"};
|
||||
#endif
|
||||
|
||||
/*! All of the relationships to be touched. */
|
||||
QStringList u_touches {"torrents"};
|
||||
};
|
||||
|
||||
#endif // TAG_H
|
||||
|
||||
@@ -128,6 +128,7 @@ private:
|
||||
/*! The storage format of the model's date columns. */
|
||||
// QString u_dateFormat {"yyyy-MM-dd HH:mm:ss"};
|
||||
/*! All of the relationships to be touched. */
|
||||
// QStringList u_touches {"tags"};
|
||||
// QStringList u_touches {"relation_name"};
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ class TorrentPeer final : public Orm::Tiny::BaseModel<TorrentPeer, Torrent>
|
||||
using BaseModel::BaseModel;
|
||||
|
||||
public:
|
||||
// CUR unify model classes look, using and similar silverqx
|
||||
/*! Get the torrent that owns the torrent peer. */
|
||||
std::unique_ptr<
|
||||
Orm::Tiny::Relations::Relation<TorrentPeer, Torrent>>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#if defined __cplusplus
|
||||
/* Add C++ includes here */
|
||||
//#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
//#include <QDir>
|
||||
#include <QHash>
|
||||
//#include <QMap>
|
||||
|
||||
Reference in New Issue
Block a user