diff --git a/docs/supported-compilers.mdx b/docs/supported-compilers.mdx
index 5de3ccf2d..128e9b915 100644
--- a/docs/supported-compilers.mdx
+++ b/docs/supported-compilers.mdx
@@ -8,7 +8,7 @@ keywords: [c++ orm, supported compilers, supported build systems, tinyorm]
# Supported Compilers
-Following compilers are backed up by the GitHub Action [workflows](https://github.com/silverqx/TinyORM/tree/main/.github/workflows) (CI pipelines), these workflows also include more then __1220 unit tests__ 😮💥.
+Following compilers are backed up by the GitHub Action [workflows](https://github.com/silverqx/TinyORM/tree/main/.github/workflows) (CI pipelines), these workflows also include more then __1223 unit tests__ 😮💥.
diff --git a/include/orm/basegrammar.hpp b/include/orm/basegrammar.hpp
index ec856fa45..d7b0c807a 100644
--- a/include/orm/basegrammar.hpp
+++ b/include/orm/basegrammar.hpp
@@ -79,6 +79,11 @@ namespace Query
/*! Get the column name without the table name, a string after last dot. */
QString unqualifyColumn(const QString &column) const;
+ /*! Get the table name without an alias. */
+ QString getFromWithoutAlias(const QString &from) const;
+ /*! Get an alias from the 'from' clause. */
+ QString getAliasFromFrom(const QString &from) const;
+
protected:
/*! Convert the vector of column names into a wrapped comma delimited string. */
template
@@ -103,10 +108,6 @@ namespace Query
/*! Get individual segments from the 'from' clause. */
QStringList getSegmentsFromFrom(const QString &from) const;
- /*! Get the table name without an alias. */
- QString getFromWithoutAlias(const QString &from) const;
- /*! Get an alias from the 'from' clause. */
- QString getAliasFromFrom(const QString &from) const;
// FEATURE qt6, use everywhere QLatin1String("") instead of = "", BUT Qt6 has char8_t ctor, so u"" can be used, I will wait with this problem silverqx
/*! The grammar table prefix. */
diff --git a/include/orm/query/querybuilder.hpp b/include/orm/query/querybuilder.hpp
index e2b7eff3c..8b6c2c68a 100644
--- a/include/orm/query/querybuilder.hpp
+++ b/include/orm/query/querybuilder.hpp
@@ -693,6 +693,9 @@ namespace Orm::Query
Builder cloneWithoutBindings(
const std::unordered_set &except) const;
+ /*! Strip off the table name or alias from a column identifier. */
+ QString stripTableForPluck(const QString &column) const;
+
protected:
/*! Determine if the given operator is supported. */
bool invalidOperator(const QString &comparison) const;
@@ -753,9 +756,6 @@ namespace Orm::Query
/*! Prepend the database name if the given query is on another database. */
Builder &prependDatabaseNameIfCrossDatabaseQuery(Builder &query) const;
- /*! Strip off the table name or alias from a column identifier. */
- QString stripTableForPluck(const QString &column) const;
-
/*! Throw an exception if the query doesn't have an orderBy clause. */
void enforceOrderBy() const;
/*! Get an array with all orders with a given column removed. */
@@ -903,9 +903,9 @@ namespace Orm::Query
if (size == 0)
return {};
- /* If the columns are qualified with a table or have an alias, we cannot use
- those directly in the "pluck" operations since the results from the DB
- are only keyed by the column itself. We'll strip the table out here. */
+ /* If the column is qualified with a table or have an alias, we cannot use
+ those directly in the "pluck" operations, we have to strip the table out or
+ use the alias name instead. */
const auto unqualifiedColumn = stripTableForPluck(column);
const auto unqualifiedKey = stripTableForPluck(key);
diff --git a/include/orm/tiny/tinybuilder.hpp b/include/orm/tiny/tinybuilder.hpp
index a769b9b9a..fd81b4dc0 100644
--- a/include/orm/tiny/tinybuilder.hpp
+++ b/include/orm/tiny/tinybuilder.hpp
@@ -7,6 +7,7 @@ TINY_SYSTEM_HEADER
#include
+#include
#include
#include
#include
@@ -73,10 +74,10 @@ namespace Orm::Tiny
QVariant soleValue(const Column &column);
/*! Get the vector with the values of a given column. */
- QVector pluck(const QString &column) const;
+ QVector pluck(const QString &column);
/*! Get the vector with the values of a given column. */
template
- std::map pluck(const QString &column, const QString &key) const;
+ std::map pluck(const QString &column, const QString &key);
/*! Find a model by its primary key. */
std::optional
@@ -335,18 +336,61 @@ namespace Orm::Tiny
}
template
- QVector Builder::pluck(const QString &column) const
+ QVector Builder::pluck(const QString &column)
{
- // CUR now, use newFromBuilder() to make and fill models and return attribute/column silverqx
- return toBase().pluck(column);
+ auto result = toBase().pluck(column);
+
+ // Nothing to pluck-ing 😎
+ if (result.empty())
+ return result;
+
+ /* If the column is qualified with a table or have an alias, we cannot use
+ those directly in the "pluck" operations, we have to strip the table out or
+ use the alias name instead. */
+ const auto unqualifiedColumn = getQuery().stripTableForPluck(column);
+
+ /* If the model has a mutator for the requested column, we will spin through
+ the results and mutate the values so that the mutated version of these
+ columns are returned as you would expect from these Eloquent models. */
+ if (!m_model.getDates().contains(unqualifiedColumn))
+ return result;
+
+ return result |= ranges::actions::transform([this, &unqualifiedColumn]
+ (auto &&value)
+ {
+ return m_model.newFromBuilder({{unqualifiedColumn,
+ std::forward(value)}})
+ .getAttribute(unqualifiedColumn);
+ });
}
template
template
std::map
- Builder::pluck(const QString &column, const QString &key) const
+ Builder::pluck(const QString &column, const QString &key)
{
- return toBase().template pluck(column, key);
+ auto result = toBase().template pluck(column, key);
+
+ // Nothing to pluck-ing 😎
+ if (result.empty())
+ return result;
+
+ /* If the column is qualified with a table or have an alias, we cannot use
+ those directly in the "pluck" operations, we have to strip the table out or
+ use the alias name instead. */
+ const auto unqualifiedColumn = getQuery().stripTableForPluck(column);
+
+ /* If the model has a mutator for the requested column, we will spin through
+ the results and mutate the values so that the mutated version of these
+ columns are returned as you would expect from these Eloquent models. */
+ if (!m_model.getDates().contains(unqualifiedColumn))
+ return result;
+
+ for (auto &&[_, value] : result)
+ value = m_model.newFromBuilder({{unqualifiedColumn, std::move(value)}})
+ .getAttribute(unqualifiedColumn);
+
+ return result;
}
// FEATURE dilemma primarykey, Model::KeyType for id silverqx
diff --git a/src/orm/basegrammar.cpp b/src/orm/basegrammar.cpp
index d55c0a49e..24f4d2c69 100644
--- a/src/orm/basegrammar.cpp
+++ b/src/orm/basegrammar.cpp
@@ -118,6 +118,16 @@ QString BaseGrammar::unqualifyColumn(const QString &column) const
return column.split(DOT).last().trimmed();
}
+QString BaseGrammar::getFromWithoutAlias(const QString &from) const
+{
+ return std::move(getSegmentsFromFrom(from).first()); // clazy:exclude=detaching-temporary
+}
+
+QString BaseGrammar::getAliasFromFrom(const QString &from) const
+{
+ return std::move(getSegmentsFromFrom(from).last()); // clazy:exclude=detaching-temporary
+}
+
/* protected */
QString BaseGrammar::parameter(const QVariant &value) const
@@ -177,22 +187,6 @@ QStringList BaseGrammar::getSegmentsFromFrom(const QString &from) const
return segments;
}
-QString BaseGrammar::getFromWithoutAlias(const QString &from) const
-{
- // Prevent clazy warning
- const auto segments = getSegmentsFromFrom(from);
-
- return segments.first();
-}
-
-QString BaseGrammar::getAliasFromFrom(const QString &from) const
-{
- // Prevent clazy warning
- const auto segments = getSegmentsFromFrom(from);
-
- return segments.last();
-}
-
} // namespace Orm
TINYORM_END_COMMON_NAMESPACE
diff --git a/src/orm/query/querybuilder.cpp b/src/orm/query/querybuilder.cpp
index f303b856f..cf3f10536 100644
--- a/src/orm/query/querybuilder.cpp
+++ b/src/orm/query/querybuilder.cpp
@@ -113,9 +113,9 @@ QVector Builder::pluck(const QString &column)
if (size == 0)
return {};
- /* If the columns are qualified with a table or have an alias, we cannot use
- those directly in the "pluck" operations since the results from the DB
- are only keyed by the column itself. We'll strip the table out here. */
+ /* If the column is qualified with a table or have an alias, we cannot use
+ those directly in the "pluck" operations, we have to strip the table out or
+ use the alias name instead. */
const auto unqualifiedColumn = stripTableForPluck(column);
QVector result;
@@ -1125,6 +1125,16 @@ Builder Builder::cloneWithoutBindings(
return copy;
}
+QString Builder::stripTableForPluck(const QString &column) const
+{
+ static const auto as = QStringLiteral(" as ");
+
+ if (!column.contains(as))
+ return m_grammar.unqualifyColumn(column);
+
+ return m_grammar.getAliasFromFrom(column);
+}
+
/* protected */
bool Builder::invalidOperator(const QString &comparison) const
@@ -1279,16 +1289,6 @@ Builder &Builder::prependDatabaseNameIfCrossDatabaseQuery(Builder &query) const
return query;
}
-QString Builder::stripTableForPluck(const QString &column) const
-{
- static const auto as = QStringLiteral(" as ");
-
- if (!column.contains(as))
- return m_grammar.unqualifyColumn(column);
-
- return column.split(as).last().trimmed();
-}
-
void Builder::enforceOrderBy() const
{
if (m_orders.isEmpty())
diff --git a/tests/auto/functional/orm/tiny/model_conn_indep/tst_model_connection_independent.cpp b/tests/auto/functional/orm/tiny/model_conn_indep/tst_model_connection_independent.cpp
index 119f3fe1e..09cf45055 100644
--- a/tests/auto/functional/orm/tiny/model_conn_indep/tst_model_connection_independent.cpp
+++ b/tests/auto/functional/orm/tiny/model_conn_indep/tst_model_connection_independent.cpp
@@ -66,6 +66,10 @@ private slots:
void pluck_EmptyResult() const;
void pluck_QualifiedColumnOrKey() const;
+ void pluck_With_u_dates() const;
+ void pluck_EmptyResult_With_u_dates() const;
+ void pluck_QualifiedColumnOrKey_With_u_dates() const;
+
/* Builds Queries */
void chunk() const;
void chunk_ReturnFalse() const;
@@ -439,15 +443,15 @@ void tst_Model_Connection_Independent::pluck() const
QCOMPARE(result, expected);
}
// Templated pluck keyed by file_index, bool type is used intentionally 😎
-// {
-// auto result = TorrentPreviewableFile::orderBy(ID)
-// ->pluck("filepath", "file_index");
-
-// std::map expected {
-// {false, "test1_file1.mkv"}, {true, "test2_file2.mkv"},
-// };
-// QCOMPARE(result, expected);
-// }
+ {
+ auto result = TorrentPreviewableFile::orderBy(ID)
+ ->pluck("filepath", "file_index");
+
+ std::map expected {
+ {false, "test1_file1.mkv"}, {true, "test2_file2.mkv"},
+ };
+ QCOMPARE(result, expected);
+ }
}
void tst_Model_Connection_Independent::pluck_EmptyResult() const
@@ -518,6 +522,142 @@ void tst_Model_Connection_Independent::pluck_QualifiedColumnOrKey() const
}
}
+void tst_Model_Connection_Independent::pluck_With_u_dates() const
+{
+ // Simple pluck without keying
+ {
+ auto result = Torrent::pluck("added_on");
+
+ QVector expected {
+ QDateTime::fromString("2020-08-01 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-02 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-03 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-04 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-05 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-06 20:11:10", Qt::ISODate),
+ };
+ QCOMPARE(result, expected);
+ }
+ // Templated pluck keyed by id and QVariant as the value
+ {
+ auto result = Torrent::pluck("added_on", ID);
+
+ std::map expected {
+ {1, QDateTime::fromString("2020-08-01 20:11:10", Qt::ISODate)},
+ {2, QDateTime::fromString("2020-08-02 20:11:10", Qt::ISODate)},
+ {3, QDateTime::fromString("2020-08-03 20:11:10", Qt::ISODate)},
+ {4, QDateTime::fromString("2020-08-04 20:11:10", Qt::ISODate)},
+ {5, QDateTime::fromString("2020-08-05 20:11:10", Qt::ISODate)},
+ {6, QDateTime::fromString("2020-08-06 20:11:10", Qt::ISODate)},
+ };
+ QCOMPARE(result, expected);
+ }
+ // Templated pluck keyed by added_on
+ {
+ auto result = Torrent::pluck(ID, "added_on");
+
+ std::map expected {
+ {QDateTime::fromString("2020-08-01 20:11:10", Qt::ISODate), 1},
+ {QDateTime::fromString("2020-08-02 20:11:10", Qt::ISODate), 2},
+ {QDateTime::fromString("2020-08-03 20:11:10", Qt::ISODate), 3},
+ {QDateTime::fromString("2020-08-04 20:11:10", Qt::ISODate), 4},
+ {QDateTime::fromString("2020-08-05 20:11:10", Qt::ISODate), 5},
+ {QDateTime::fromString("2020-08-06 20:11:10", Qt::ISODate), 6},
+ };
+ QCOMPARE(result, expected);
+ }
+}
+
+void tst_Model_Connection_Independent::pluck_EmptyResult_With_u_dates() const
+{
+ {
+ auto result = Torrent::whereEq(NAME, "dummy-NON_EXISTENT")->pluck("added_on");
+
+ QCOMPARE(result, QVector());
+ }
+ {
+ auto result = Torrent::whereEq(NAME, "dummy-NON_EXISTENT")
+ ->pluck(ID, "added_on");
+
+ std::map expected;
+ QCOMPARE(result, expected);
+ }
+}
+
+void tst_Model_Connection_Independent::pluck_QualifiedColumnOrKey_With_u_dates() const
+{
+ // Strip table name
+ {
+ auto result = Torrent::pluck("torrents.added_on");
+
+ QVector expected {
+ QDateTime::fromString("2020-08-01 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-02 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-03 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-04 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-05 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-06 20:11:10", Qt::ISODate),
+ };
+ QCOMPARE(result, expected);
+ }
+ // Strip table name
+ {
+ auto result = Torrent::pluck("torrents.added_on", ID);
+
+ std::map expected {
+ {1, QDateTime::fromString("2020-08-01 20:11:10", Qt::ISODate)},
+ {2, QDateTime::fromString("2020-08-02 20:11:10", Qt::ISODate)},
+ {3, QDateTime::fromString("2020-08-03 20:11:10", Qt::ISODate)},
+ {4, QDateTime::fromString("2020-08-04 20:11:10", Qt::ISODate)},
+ {5, QDateTime::fromString("2020-08-05 20:11:10", Qt::ISODate)},
+ {6, QDateTime::fromString("2020-08-06 20:11:10", Qt::ISODate)},
+ };
+ QCOMPARE(result, expected);
+ }
+ // Strip column alias
+ {
+ auto result = Torrent::orderBy(ID)->pluck("added_on as added_on_alt");
+
+ QVector expected {
+ QDateTime::fromString("2020-08-01 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-02 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-03 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-04 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-05 20:11:10", Qt::ISODate),
+ QDateTime::fromString("2020-08-06 20:11:10", Qt::ISODate),
+ };
+ QCOMPARE(result, expected);
+ }
+ // Strip column alias
+ {
+ auto result = Torrent::pluck("added_on as added_on_alt", "id as id_alt");
+
+ std::map expected {
+ {1, QDateTime::fromString("2020-08-01 20:11:10", Qt::ISODate)},
+ {2, QDateTime::fromString("2020-08-02 20:11:10", Qt::ISODate)},
+ {3, QDateTime::fromString("2020-08-03 20:11:10", Qt::ISODate)},
+ {4, QDateTime::fromString("2020-08-04 20:11:10", Qt::ISODate)},
+ {5, QDateTime::fromString("2020-08-05 20:11:10", Qt::ISODate)},
+ {6, QDateTime::fromString("2020-08-06 20:11:10", Qt::ISODate)},
+ };
+ QCOMPARE(result, expected);
+ }
+ // Strip column alias and table name
+ {
+ auto result = Torrent::pluck("torrents.added_on", "id as id_alt");
+
+ std::map expected {
+ {1, QDateTime::fromString("2020-08-01 20:11:10", Qt::ISODate)},
+ {2, QDateTime::fromString("2020-08-02 20:11:10", Qt::ISODate)},
+ {3, QDateTime::fromString("2020-08-03 20:11:10", Qt::ISODate)},
+ {4, QDateTime::fromString("2020-08-04 20:11:10", Qt::ISODate)},
+ {5, QDateTime::fromString("2020-08-05 20:11:10", Qt::ISODate)},
+ {6, QDateTime::fromString("2020-08-06 20:11:10", Qt::ISODate)},
+ };
+ QCOMPARE(result, expected);
+ }
+}
+
/* Builds Queries */
void tst_Model_Connection_Independent::chunk() const
diff --git a/tests/models/models/torrent.hpp b/tests/models/models/torrent.hpp
index e4a0055ef..b8dbfc86e 100644
--- a/tests/models/models/torrent.hpp
+++ b/tests/models/models/torrent.hpp
@@ -185,7 +185,7 @@ class Torrent final :
// inline static QString u_dateFormat {"yyyy-MM-dd HH:mm:ss"};
/*! The attributes that should be mutated to dates. */
- inline static const QStringList u_dates {"added_on"};
+ inline static const QStringList u_dates {"added_on", "added_on_alt"};
/*! All of the relationships to be touched. */
// QStringList u_touches {"tags"};