Skip to content

Commit

Permalink
bugfix dangling reference πŸ˜΅β€πŸ’«πŸ˜²πŸ™ƒ
Browse files Browse the repository at this point in the history
The QueryGrammar instance was dangling reference after a connection was
removed and created again.
  • Loading branch information
silverqx committed Mar 12, 2023
1 parent 065333b commit 60bbaab
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 71 deletions.
8 changes: 5 additions & 3 deletions include/orm/query/grammars/grammar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,19 @@ namespace Orm::Query::Grammars
struct SelectComponentValue
{
/*! The component's compile method. */
std::function<QString(const QueryBuilder &)> compileMethod;
std::function<QString(const Grammar &, const QueryBuilder &)> compileMethod;
/*! Determine whether the component is set and is not empty. */
std::function<bool(const QueryBuilder &)> isset;
};
/*! Alias type for the whereXx() methods. */
using WhereMemFn = std::function<QString(const Grammar &grammar,
const WhereConditionItem &)>;

/*! Map the ComponentType to a Grammar::compileXx() methods. */
virtual const QMap<SelectComponentType, SelectComponentValue> &
getCompileMap() const = 0;
/*! Map the WhereType to a Grammar::whereXx() methods. */
virtual const std::function<QString(const WhereConditionItem &)> &
getWhereMethod(WhereType whereType) const = 0;
virtual const WhereMemFn &getWhereMethod(WhereType whereType) const = 0;

/*! Determine whether the 'aggregate' component should be compiled. */
static bool shouldCompileAggregate(const std::optional<AggregateItem> &aggregate);
Expand Down
3 changes: 1 addition & 2 deletions include/orm/query/grammars/mysqlgrammar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ namespace Orm::Query::Grammars
const QMap<SelectComponentType, SelectComponentValue> &
getCompileMap() const override;
/*! Map the WhereType to a Grammar::whereXx() methods. */
const std::function<QString(const WhereConditionItem &)> &
getWhereMethod(WhereType whereType) const override;
const WhereMemFn &getWhereMethod(WhereType whereType) const override;

/*! Compile an update statement without joins into SQL. */
QString
Expand Down
3 changes: 1 addition & 2 deletions include/orm/query/grammars/postgresgrammar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ namespace Orm::Query::Grammars
const QMap<SelectComponentType, SelectComponentValue> &
getCompileMap() const override;
/*! Map the WhereType to a Grammar::whereXx() methods. */
const std::function<QString(const WhereConditionItem &)> &
getWhereMethod(WhereType whereType) const override;
const WhereMemFn &getWhereMethod(WhereType whereType) const override;

/*! Compile the "select *" portion of the query. */
QString compileColumns(const QueryBuilder &query) const override;
Expand Down
3 changes: 1 addition & 2 deletions include/orm/query/grammars/sqlitegrammar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ namespace Orm::Query::Grammars
const QMap<SelectComponentType, SelectComponentValue> &
getCompileMap() const override;
/*! Map the WhereType to a Grammar::whereXx() methods. */
const std::function<QString(const WhereConditionItem &)> &
getWhereMethod(WhereType whereType) const override;
const WhereMemFn &getWhereMethod(WhereType whereType) const override;

/*! Compile a "where date" clause. */
QString whereDate(const WhereConditionItem &where) const;
Expand Down
4 changes: 2 additions & 2 deletions src/orm/query/grammars/grammar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ QStringList Grammar::compileComponents(const QueryBuilder &query) const

for (const auto &component : compileMap)
if (component.isset && component.isset(query))
sql << std::invoke(component.compileMethod, query);
sql << std::invoke(component.compileMethod, *this, query);

return sql;
}
Expand Down Expand Up @@ -268,7 +268,7 @@ QStringList Grammar::compileWheresToVector(const QueryBuilder &query) const

for (const auto &where : wheres)
compiledWheres << SPACE_IN.arg(where.condition,
std::invoke(getWhereMethod(where.type), where));
std::invoke(getWhereMethod(where.type), *this, where));

return compiledWheres;
}
Expand Down
34 changes: 18 additions & 16 deletions src/orm/query/grammars/mysqlgrammar.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "orm/query/grammars/mysqlgrammar.hpp"

#include "orm/macros/threadlocal.hpp"
#include "orm/mysqlconnection.hpp"
#include "orm/query/querybuilder.hpp"

Expand Down Expand Up @@ -105,18 +104,20 @@ MySqlGrammar::getCompileMap() const
'this' reference and the compileMethod rvalue reference in the following lambda
and simply save std::function<> in the SelectComponentValue's compileMethod data
member. */
const auto bind = [this](auto &&compileMethod)
const auto bind = [](auto &&compileMethod)
{
return [this,
compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const auto &query)
return [compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const Grammar &grammar, const QueryBuilder &query)
{
return std::invoke(compileMethod, this, query);
/* We can be at 100% sure that this is the MySqlGrammar instance because
this method is virtual; used the reinterpret_cast<> to avoid useless
and slower dynamic_cast<>. */
return std::invoke(compileMethod,
reinterpret_cast<const MySqlGrammar &>(grammar), query);
};
};

// Pointers to a where member methods by whereType, yes yes c++ πŸ˜‚
T_THREAD_LOCAL
static const QMap<SelectComponentType, SelectComponentValue> cached {
{SelectComponentType::AGGREGATE, {bind(&MySqlGrammar::compileAggregate),
[](const auto &query)
Expand Down Expand Up @@ -148,28 +149,30 @@ MySqlGrammar::getCompileMap() const
return cached;
}

const std::function<QString(const WhereConditionItem &)> &
const Grammar::WhereMemFn &
MySqlGrammar::getWhereMethod(const WhereType whereType) const
{
/* Needed, because some compileXx() methods are overloaded, this way I will capture
'this' reference and the compileMethod rvalue reference in the following lambda
and simply save std::function<> in the SelectComponentValue's compileMethod data
member. */
const auto bind = [this](auto &&compileMethod)
const auto bind = [](auto &&compileMethod)
{
return [this,
compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const auto &query)
return [compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const Grammar &grammar, const WhereConditionItem &where)
{
return std::invoke(compileMethod, this, query);
/* We can be at 100% sure that this is the MySqlGrammar instance because
this method is virtual; used the reinterpret_cast<> to avoid useless
and slower dynamic_cast<>. */
return std::invoke(compileMethod,
reinterpret_cast<const MySqlGrammar &>(grammar), where);
};
};

// Pointers to a where member methods by whereType, yes yes c++ πŸ˜‚
// An order has to be the same as in enum struct WhereType
// FUTURE QHash would has faster lookup, I should choose QHash, fix also another Grammars silverx
T_THREAD_LOCAL
static const QVector<std::function<QString(const WhereConditionItem &)>> cached {
static const QVector<WhereMemFn> cached {
bind(&MySqlGrammar::whereBasic),
bind(&MySqlGrammar::whereNested),
bind(&MySqlGrammar::whereColumn),
Expand All @@ -190,7 +193,6 @@ MySqlGrammar::getWhereMethod(const WhereType whereType) const
bind(&MySqlGrammar::whereYear),
};

T_THREAD_LOCAL
static const auto size = cached.size();

// Check if whereType is in the range, just for sure 😏
Expand Down
34 changes: 18 additions & 16 deletions src/orm/query/grammars/postgresgrammar.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "orm/query/grammars/postgresgrammar.hpp"

#include "orm/macros/threadlocal.hpp"
#include "orm/query/querybuilder.hpp"

TINYORM_BEGIN_COMMON_NAMESPACE
Expand Down Expand Up @@ -113,18 +112,20 @@ PostgresGrammar::getCompileMap() const
'this' reference and the compileMethod rvalue reference in the following lambda
and simply save std::function<> in the SelectComponentValue's compileMethod data
member. */
const auto bind = [this](auto &&compileMethod)
const auto bind = [](auto &&compileMethod)
{
return [this,
compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const auto &query)
return [compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const Grammar &grammar, const QueryBuilder &query)
{
return std::invoke(compileMethod, this, query);
/* We can be at 100% sure that this is the PostgresGrammar instance because
this method is virtual; used the reinterpret_cast<> to avoid useless
and slower dynamic_cast<>. */
return std::invoke(compileMethod,
reinterpret_cast<const PostgresGrammar &>(grammar), query);
};
};

// Pointers to a where member methods by whereType, yes yes c++ πŸ˜‚
T_THREAD_LOCAL
static const QMap<SelectComponentType, SelectComponentValue> cached {
{SelectComponentType::AGGREGATE, {bind(&PostgresGrammar::compileAggregate),
[](const auto &query)
Expand Down Expand Up @@ -155,28 +156,30 @@ PostgresGrammar::getCompileMap() const
return cached;
}

const std::function<QString(const WhereConditionItem &)> &
const Grammar::WhereMemFn &
PostgresGrammar::getWhereMethod(const WhereType whereType) const
{
/* Needed, because some compileXx() methods are overloaded, this way I will capture
'this' reference and the compileMethod rvalue reference in the following lambda
and simply save std::function<> in the SelectComponentValue's compileMethod data
member. */
const auto bind = [this](auto &&compileMethod)
const auto bind = [](auto &&compileMethod)
{
return [this,
compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const auto &query)
return [compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const Grammar &grammar, const WhereConditionItem &query)
{
return std::invoke(compileMethod, this, query);
/* We can be at 100% sure that this is the PostgresGrammar instance because
this method is virtual; used the reinterpret_cast<> to avoid useless
and slower dynamic_cast<>. */
return std::invoke(compileMethod,
reinterpret_cast<const PostgresGrammar &>(grammar), query);
};
};

// Pointers to a where member methods by whereType, yes yes c++ πŸ˜‚
// An order has to be the same as in enum struct WhereType
// FUTURE QHash would has faster lookup, I should choose QHash, fix also another Grammars silverx
T_THREAD_LOCAL
static const QVector<std::function<QString(const WhereConditionItem &)>> cached {
static const QVector<WhereMemFn> cached {
bind(&PostgresGrammar::whereBasic),
bind(&PostgresGrammar::whereNested),
bind(&PostgresGrammar::whereColumn),
Expand All @@ -197,7 +200,6 @@ PostgresGrammar::getWhereMethod(const WhereType whereType) const
bind(&PostgresGrammar::whereYear),
};

T_THREAD_LOCAL
static const auto size = cached.size();

// Check if whereType is in the range, just for sure 😏
Expand Down
34 changes: 18 additions & 16 deletions src/orm/query/grammars/sqlitegrammar.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "orm/query/grammars/sqlitegrammar.hpp"

#include "orm/macros/threadlocal.hpp"
#include "orm/query/querybuilder.hpp"

TINYORM_BEGIN_COMMON_NAMESPACE
Expand Down Expand Up @@ -92,18 +91,20 @@ SQLiteGrammar::getCompileMap() const
'this' reference and the compileMethod rvalue reference in the following lambda
and simply save std::function<> in the SelectComponentValue's compileMethod data
member. */
const auto bind = [this](auto &&compileMethod)
const auto bind = [](auto &&compileMethod)
{
return [this,
compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const auto &query)
return [compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const Grammar &grammar, const QueryBuilder &query)
{
return std::invoke(compileMethod, this, query);
/* We can be at 100% sure that this is the SQLiteGrammar instance because
this method is virtual; used the reinterpret_cast<> to avoid useless
and slower dynamic_cast<>. */
return std::invoke(compileMethod,
reinterpret_cast<const SQLiteGrammar &>(grammar), query);
};
};

// Pointers to a where member methods by whereType, yes yes c++ πŸ˜‚
T_THREAD_LOCAL
static const QMap<SelectComponentType, SelectComponentValue> cached {
{SelectComponentType::AGGREGATE, {bind(&SQLiteGrammar::compileAggregate),
[](const auto &query)
Expand Down Expand Up @@ -134,27 +135,29 @@ SQLiteGrammar::getCompileMap() const
return cached;
}

const std::function<QString(const WhereConditionItem &)> &
const Grammar::WhereMemFn &
SQLiteGrammar::getWhereMethod(const WhereType whereType) const
{
/* Needed, because some compileXx() methods are overloaded, this way I will capture
'this' reference and the compileMethod rvalue reference in the following lambda
and simply save std::function<> in the SelectComponentValue's compileMethod data
member. */
const auto bind = [this](auto &&compileMethod)
const auto bind = [](auto &&compileMethod)
{
return [this,
compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const auto &query)
return [compileMethod = std::forward<decltype (compileMethod)>(compileMethod)]
(const Grammar &grammar, const WhereConditionItem &query)
{
return std::invoke(compileMethod, this, query);
/* We can be at 100% sure that this is the SQLiteGrammar instance because
this method is virtual; used the reinterpret_cast<> to avoid useless
and slower dynamic_cast<>. */
return std::invoke(compileMethod,
reinterpret_cast<const SQLiteGrammar &>(grammar), query);
};
};

// Pointers to a where member methods by whereType, yes yes c++ πŸ˜‚
// An order has to be the same as in enum struct WhereType
T_THREAD_LOCAL
static const QVector<std::function<QString(const WhereConditionItem &)>> cached {
static const QVector<WhereMemFn> cached {
bind(&SQLiteGrammar::whereBasic),
bind(&SQLiteGrammar::whereNested),
bind(&SQLiteGrammar::whereColumn),
Expand All @@ -175,7 +178,6 @@ SQLiteGrammar::getWhereMethod(const WhereType whereType) const
bind(&SQLiteGrammar::whereYear),
};

T_THREAD_LOCAL
static const auto size = cached.size();

// Check if whereType is in the range, just for sure 😏
Expand Down
2 changes: 0 additions & 2 deletions src/orm/schema/grammars/mysqlschemagrammar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <unordered_set>

#include "orm/databaseconnection.hpp"
#include "orm/macros/threadlocal.hpp"
#include "orm/utils/type.hpp"

TINYORM_BEGIN_COMMON_NAMESPACE
Expand Down Expand Up @@ -281,7 +280,6 @@ MySqlSchemaGrammar::invokeCompileMethod(const CommandDefinition &command,
I have to map by QString instead of enum struct because a command.name is used
to look up, I could use enum struct but I would have to map
QString(command.name) -> enum. */
T_THREAD_LOCAL
static const std::unordered_map<QString, CompileMemFn> cached {
{Add, bind(&MySqlSchemaGrammar::compileAdd)},
{Rename, bind(&MySqlSchemaGrammar::compileRename)},
Expand Down
2 changes: 0 additions & 2 deletions src/orm/schema/grammars/postgresschemagrammar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <unordered_set>

#include "orm/databaseconnection.hpp"
#include "orm/macros/threadlocal.hpp"
#include "orm/utils/type.hpp"

TINYORM_BEGIN_COMMON_NAMESPACE
Expand Down Expand Up @@ -338,7 +337,6 @@ PostgresSchemaGrammar::invokeCompileMethod(const CommandDefinition &command,
I have to map by QString instead of enum struct because a command.name is used
to look up, I could use enum struct but I would have to map
QString(command.name) -> enum. */
T_THREAD_LOCAL
static const std::unordered_map<QString, CompileMemFn> cached {
{Add, bind(&PostgresSchemaGrammar::compileAdd)},
{Rename, bind(&PostgresSchemaGrammar::compileRename)},
Expand Down
2 changes: 0 additions & 2 deletions src/orm/schema/grammars/sqliteschemagrammar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <range/v3/view/remove_if.hpp>

#include "orm/exceptions/runtimeerror.hpp"
#include "orm/macros/threadlocal.hpp"
#include "orm/schema/blueprint.hpp"
#include "orm/utils/type.hpp"

Expand Down Expand Up @@ -249,7 +248,6 @@ SQLiteSchemaGrammar::invokeCompileMethod(const CommandDefinition &command,
I have to map by QString instead of enum struct because a command.name is used
to look up, I could use enum struct but I would have to map
QString(command.name) -> enum. */
T_THREAD_LOCAL
static const std::unordered_map<QString, CompileMemFn> cached {
{Add, bind(&SQLiteSchemaGrammar::compileAdd)},
{Rename, bind(&SQLiteSchemaGrammar::compileRename)},
Expand Down
Loading

0 comments on commit 60bbaab

Please sign in to comment.