33 #include <type_traits> 35 #include <boost/fusion/include/for_each.hpp> 36 #include <boost/fusion/include/fold.hpp> 37 #include <boost/fusion/include/filter_if.hpp> 38 #include <boost/fusion/container/vector.hpp> 39 #include <boost/fusion/include/vector.hpp> 40 #include <boost/fusion/include/transform.hpp> 41 #include <boost/fusion/include/zip.hpp> 42 #include <boost/fusion/container/generation/make_vector.hpp> 43 #include <boost/variant/variant.hpp> 44 #include <boost/optional.hpp> 45 #include <QStringList> 78 :
std::runtime_error (str)
106 if constexpr (IsDetected_v<MorpherDetector, T>)
107 return T::FieldNameMorpher (str);
110 if (str.endsWith (
'_'))
116 template<
typename Seq,
int Idx>
121 return MorphFieldName<Seq> (boost::fusion::extension::struct_member_name<Seq, Idx>::call ());
126 constexpr
auto SeqSize = boost::fusion::result_of::size<S>::type::value;
129 constexpr
auto SeqIndices = std::make_index_sequence<SeqSize<S>> {};
136 return Run (SeqIndices<S>);
139 template<
size_t... Vals>
140 QStringList Run (std::index_sequence<Vals...>)
const 146 template<
typename Seq,
int Idx>
158 static constexpr
auto Ptr ()
166 return &boost::fusion::at_c<Idx> (
Obj_);
170 template<auto Ptr,
size_t Idx = 0>
175 if constexpr (Idx == SeqSize<S>)
176 throw std::runtime_error {
"wut, no such field?" };
181 if constexpr (std::is_same_v<decltype (direct), decltype (indexed)>)
183 if (indexed == direct)
187 return FieldIndex<Ptr, Idx + 1> ();
192 template<
typename T,
typename =
void>
199 else if constexpr (std::is_same_v<T, double>)
201 else if constexpr (std::is_same_v<T, QString> || std::is_same_v<T, QDateTime>)
203 else if constexpr (std::is_same_v<T, QByteArray>)
206 static_assert (std::is_same_v<T, struct Dummy>,
"Unsupported type");
222 template<
typename T,
typename... Tags>
228 template<
typename... Tags>
240 constexpr
auto idx = detail::FieldIndex<Ptr> ();
246 template<
typename T,
typename =
void>
251 if constexpr (std::is_same_v<T, QDateTime>)
252 return t.toString (Qt::ISODate);
253 else if constexpr (std::is_enum_v<T>)
254 return static_cast<qint64
> (t);
262 template<
typename T,
typename =
void>
267 if constexpr (std::is_same_v<T, QDateTime>)
268 return QDateTime::fromString (var.toString (), Qt::ISODate);
269 else if constexpr (std::is_enum_v<T>)
270 return static_cast<T
> (var.value<qint64> ());
274 return var.value<T> ();
308 const auto& qualified =
Util::Map (
fields, [&table] (
const QString& field) {
return table +
"." + field; });
309 const auto& boundFields =
Util::Map (
fields, [] (
const QString& str) {
return ':' + str; });
311 return { table,
fields, qualified, boundFields };
317 static CachedFieldsData result = BuildCachedFieldsData<T> (T::ClassName ());
324 return [data, insertQuery, bindPrimaryKey] (
const T& t)
327 [&] (
auto pos,
const auto& elem)
329 using Elem = std::decay_t<decltype (elem)>;
335 if (!insertQuery->exec ())
338 throw QueryException (
"insert query execution failed", insertQuery);
343 template<
typename Seq,
int Idx>
344 using ValueAtC_t =
typename boost::fusion::result_of::value_at_c<Seq, Idx>::type;
346 template<
typename Seq,
typename Idx>
347 using ValueAt_t =
typename boost::fusion::result_of::value_at<Seq, Idx>::type;
349 template<
typename Seq,
typename MemberIdx = boost::mpl::
int_<0>>
352 static_assert ((boost::fusion::result_of::size<Seq>::value) != (MemberIdx::value),
353 "Primary key not found");
368 template<
typename Seq>
371 template<
typename Seq>
372 constexpr
auto HasPKey = IsDetected_v<FindPKeyDetector, Seq>;
374 template<
typename Seq>
377 if constexpr (HasPKey<Seq>)
390 return "INSERT OR IGNORE";
392 return "INSERT OR REPLACE";
398 template<
typename Seq>
407 mutable std::array<QSqlQuery_ptr, InsertActionCount>
Queries_;
418 data.Fields_.removeAt (index);
419 data.BoundFields_.removeAt (index);
432 return Run<true> (t, action);
437 return Run<false> (t, action);
440 template<
bool UpdatePKey,
typename Val>
443 const auto query = GetQuery (action);
452 if constexpr (UpdatePKey)
453 boost::fusion::at_c<index> (t) = lastId;
461 auto& query =
Queries_ [
static_cast<size_t> (action)];
464 query = std::make_shared<QSqlQuery> (
DB_);
471 template<
typename Seq,
bool HasPKey = HasPKey<Seq>>
476 template<
bool B = HasPKey>
482 const auto& del =
"DELETE FROM " + data.
Table_ +
483 " WHERE " + data.
Fields_.at (index) +
" = " + boundName +
";";
485 const auto deleteQuery = std::make_shared<QSqlQuery> (db);
486 deleteQuery->prepare (del);
488 Deleter_ = [deleteQuery, boundName] (
const Seq& t)
491 deleteQuery->bindValue (boundName,
ToVariantF (boost::fusion::at_c<index> (t)));
492 if (!deleteQuery->exec ())
493 throw QueryException (
"delete query execution failed", deleteQuery);
497 template<
bool B = HasPKey>
502 template<
bool B = HasPKey>
509 template<
typename T,
typename... Args>
512 template<
typename T,
size_t... Indices>
520 const auto dummy = std::initializer_list<int>
529 template<
int HeadT,
int... TailT>
543 template<
typename FieldsUnpacker,
typename HeadArg,
typename... TailArgs>
549 void operator() (
const HeadArg& arg,
const TailArgs&... tail)
const 557 template<
typename FieldsUnpacker,
typename HeadArg>
611 return "invalid type";
645 template<
typename Seq,
typename L,
typename R>
649 template<
typename Seq,
typename L,
typename R>
650 constexpr
auto AreComparableTypes = IsDetected_v<ComparableDetector, Seq, L, R> || IsDetected_v<ComparableDetector, Seq, R, L>;
652 template<
typename Seq,
typename L,
typename R,
typename =
void>
655 template<
typename Seq,
typename L,
typename R>
658 template<ExprType Type,
typename Seq,
typename L,
typename R,
typename =
void>
661 template<ExprType Type,
typename Seq,
typename L,
typename R>
664 template<ExprType Type,
typename L =
void,
typename R =
void>
670 template<ExprType Type,
typename L,
typename R>
673 template<
typename L,
typename R>
689 return Left_.GetFieldName () +
" = " + Right_.ToSql (state);
691 return Left_.ToSql (state) +
", " + Right_.ToSql (state);
694 template<
typename OL,
typename OR>
701 template<ExprType Type,
typename L,
typename R>
717 "Incompatible types passed to a relational operator.");
719 return Left_.ToSql (state) +
" " +
TypeToSql (
Type) +
" " + Right_.ToSql (state);
725 return Left_.template AdditionalTables<T> () + Right_.template AdditionalTables<T> ();
739 static_assert (Idx < boost::fusion::result_of::size<T>::type::value,
"Index out of bounds.");
750 template<
auto... Ptr>
769 constexpr
auto idx = FieldIndex<Ptr> ();
777 if constexpr (std::is_same_v<Seq, T>)
780 return { Seq::ClassName () };
784 auto operator= (
const R&)
const;
800 template<
typename ObjT>
803 const auto& name =
":bound_" + QString::number (++state.
LastID_);
836 template<ExprType Type,
typename L,
typename R>
839 return { left, right };
842 template<
typename L,
typename R>
845 template<
typename L,
typename R>
848 template<
typename L,
typename R,
typename = EnableRelOp_t<L, R>>
851 if constexpr (AllTrees_v<L, R>)
852 return MakeExprTree<ExprType::Less> (left, right);
857 template<
typename L,
typename R,
typename = EnableRelOp_t<L, R>>
860 if constexpr (AllTrees_v<L, R>)
861 return MakeExprTree<ExprType::Greater> (left, right);
866 template<
typename L,
typename R,
typename = EnableRelOp_t<L, R>>
869 if constexpr (AllTrees_v<L, R>)
870 return MakeExprTree<ExprType::Equal> (left, right);
875 template<
typename L,
typename R,
typename = EnableRelOp_t<L, R>>
878 if constexpr (AllTrees_v<L, R>)
879 return MakeExprTree<ExprType::And> (left, right);
887 return std::tuple { QString {},
Void {}, lastId };
890 template<
typename Seq,
typename Tree,
891 typename = decltype (std::declval<Tree> ().ToSql (std::declval<ToSqlState<Seq>&> ()))>
896 const auto& sql = tree.ToSql (state);
901 [state] (QSqlQuery& query)
903 for (
const auto& pair :
Stlize (state.BoundMembers_))
904 query.bindValue (pair.first, pair.second);
915 template<AggregateFunction>
924 static constexpr
pos<0> _0 = {};
925 static constexpr pos<1> _1 = {};
926 static constexpr pos<2> _2 = {};
927 static constexpr pos<3> _3 = {};
928 static constexpr pos<4> _4 = {};
933 template<
auto... Ptrs>
941 template<
auto... Ptrs,
size_t... Idxs>
944 return [] (
const QSqlQuery& q)
946 if constexpr (
sizeof... (Ptrs) == 1)
953 template<
auto... Ptrs>
956 return { BuildCachedFieldsData<MemberPtrStruct_t<Ptrs>> ().QualifiedFields_.value (FieldIndex<Ptrs> ())... };
961 template<
typename T, SelectBehaviour SelectBehaviour>
964 const QSqlDatabase DB_;
967 struct SelectWhole {};
980 template<
typename Single>
983 if constexpr (
IsExprTree<std::decay_t<Single>> {})
984 return (*
this) (SelectWhole {}, std::forward<Single> (single));
989 template<
typename Selector, ExprType Type,
typename L,
typename R>
992 const auto& [where, binder, _] = HandleExprTree<T> (tree);
994 const auto& [
fields, initializer, postproc] = HandleSelector (std::forward<Selector> (selector));
995 return postproc (Select (
fields, BuildFromClause (tree), where, binder, initializer));
998 template<
typename Binder,
typename Initializer>
999 auto Select (
const QString&
fields,
const QString& from, QString where,
1000 Binder&& binder, Initializer&& initializer)
const 1002 if (!where.isEmpty ())
1003 where.prepend (
" WHERE ");
1005 const auto& queryStr =
"SELECT " +
fields +
1009 QSqlQuery query { DB_ };
1010 query.prepare (queryStr);
1011 if constexpr (!std::is_same_v<
Void, std::decay_t<Binder>>)
1015 throw
QueryException ("fetch query execution failed",
std::make_shared<QSqlQuery> (query));
1020 while (query.next ())
1021 result << initializer (query);
1026 using RetType_t = boost::optional<std::result_of_t<Initializer (QSqlQuery)>>;
1027 return query.next () ?
1033 template<ExprType Type,
typename L,
typename R>
1034 QString BuildFromClause (
const ExprTree<Type, L, R>& tree)
const 1038 const auto& additionalTables = Util::MapAs<QList> (tree.template AdditionalTables<T> (),
1039 [] (
const QString& table) {
return ", " + table; });
1040 return Cached_.
Table_ + additionalTables.join (QString {});
1046 auto HandleSelector (SelectWhole)
const 1051 [] (
const QSqlQuery& q) {
return InitializeFromQuery<T> (q, SeqIndices<T>); },
1057 auto HandleSelector (sph::pos<Idx>)
const 1062 [] (
const QSqlQuery& q) {
return FromVariant<UnwrapIndirect_t<ValueAtC_t<T, Idx>>> {} (q.value (0)); },
1067 template<
auto... Ptrs>
1068 auto HandleSelector (MemberPtrs<Ptrs...> ptrs)
const 1078 template<AggregateFunction Fun>
1079 auto HandleSelector (AggregateType<Fun>)
const 1084 QString {
"count(1)" },
1085 [] (
const QSqlQuery& q) {
return q.value (0).toLongLong (); },
1091 template<
typename T>
1094 const QSqlDatabase DB_;
1103 template<ExprType Type,
typename L,
typename R>
1106 const auto& [where, binder, _] = HandleExprTree<T> (tree);
1109 const auto& selectAll =
"DELETE FROM " + Cached_.
Table_ +
1110 " WHERE " + where +
";";
1112 QSqlQuery query { DB_ };
1113 query.prepare (selectAll);
1119 template<
typename T,
bool HasPKey = HasPKey<T>>
1122 const QSqlDatabase DB_;
1125 std::function<void (T)> Updater_;
1133 const auto index = FindPKey<T>::result_type::value;
1138 const auto& fieldName = removedFields.takeAt (index);
1139 const auto& boundName = removedBoundFields.takeAt (index);
1141 const auto& statements =
Util::ZipWith (removedFields, removedBoundFields,
1142 [] (
const QString& s1,
const QString& s2) {
return s1 +
" = " + s2; });
1144 const auto& update =
"UPDATE " + data.Table_ +
1145 " SET " + statements.join (
", ") +
1146 " WHERE " + fieldName +
" = " + boundName +
";";
1148 const auto updateQuery = std::make_shared<QSqlQuery> (db);
1149 updateQuery->prepare (update);
1150 Updater_ = MakeInserter<T> (data, updateQuery,
true);
1154 template<
bool B = HasPKey>
1160 template<
typename SL,
typename SR, ExprType WType,
typename WL,
typename WR>
1163 const auto& [setClause, setBinder, setLast] = HandleExprTree<T> (
set);
1164 const auto& [whereClause, whereBinder, _] = HandleExprTree<T> (where, setLast);
1166 const auto& update =
"UPDATE " + Cached_.
Table_ +
1167 " SET " + setClause +
1168 " WHERE " + whereClause;
1170 QSqlQuery query { DB_ };
1171 query.prepare (update);
1173 whereBinder (query);
1178 template<
typename T>
1181 template<
typename T>
1184 template<
typename T>
1187 template<
int... Fields>
1192 return "UNIQUE (" + QStringList { data.
Fields_.value (Fields)... }.join (
", ") +
")";
1196 template<
int... Fields>
1201 return "PRIMARY KEY (" + QStringList { data.
Fields_.value (Fields)... }.join (
", ") +
")";
1205 template<
typename... Args>
1211 template<
typename T,
size_t... Indices>
1217 template<
typename T>
1220 const auto& types = GetTypes<T> (SeqIndices<T>);
1223 const auto& constraintsStr = constraints.isEmpty () ?
1225 (
", " + constraints.join (
", "));
1228 [] (
const QString& type,
const QString& field) { return field +
" " + type; });
1229 return "CREATE TABLE " +
1232 statements.join (
", ") +
1238 template<
typename T>
1250 template<
typename T>
1253 const auto& cachedData = detail::BuildCachedFieldsData<T> ();
1255 if (db.record (cachedData.Table_).isEmpty ())
1256 RunTextQuery (db, detail::AdaptCreateTable<T> (cachedData));
1269 template<
typename T>
1272 template<
typename T>
1275 return std::make_shared<ObjectInfo<T>> (Adapt<T> (db));
std::enable_if_t< AnyOf< IsExprTree, L, R > > EnableRelOp_t
detail::DeleteByFieldsWrapper< T > DeleteBy
constexpr detail::AggregateType< detail::AggregateFunction::Count > count
virtual ~QueryException()
QString operator()() const
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
auto Stlize(Assoc &&assoc) -> detail::StlAssocRange< detail::Identity, detail::Identity, decltype(assoc.begin()), Assoc, PairType >
Converts an Qt's associative sequence assoc to an STL-like iteratable range.
detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, boost::mpl::int_< Idx > > pos
std::array< QSqlQuery_ptr, InsertActionCount > Queries_
AdaptInsert(const QSqlDatabase &db, CachedFieldsData data)
const CachedFieldsData Data_
std::shared_ptr< ObjectInfo< T > > ObjectInfo_ptr
typename std::conditional_t< IsPKey< ValueAt_t< Seq, MemberIdx > >::value, Lazy< MemberIdx >, Lazy< FindPKey< Seq, typename boost::mpl::next< MemberIdx >::type > > >::type result_type
QString ToSql(ToSqlState< T > &) const
constexpr size_t InsertActionCount
Util::IsDetected_t< Constraints<>, ConstraintsDetector, T > ConstraintsType
auto ZipWith(const Container< T1 > &c1, const Container< T2 > &c2, F f) -> WrapType_t< Container< std::decay_t< std::result_of_t< F(T1, T2)>>>>
AdaptDelete(const QSqlDatabase &, const CachedFieldsData &, std::enable_if_t<!B > *=nullptr)
QString TypeToSql(ExprType type)
typename detail::DecomposeMemberPtr< decltype(Ptr)>::StructType_t MemberPtrStruct_t
auto operator==(const L &left, const R &right)
QVariant ToVariantF(const T &t)
typename T::Constraints ConstraintsDetector
T InitializeFromQuery(const QSqlQuery &q, std::index_sequence< Indices... >)
const QSqlQuery & GetQuery() const
static constexpr auto Ptr()
detail::SelectWrapper< T, detail::SelectBehaviour::Some > Select
Typelist< Args... > Constraints
constexpr auto AllTrees_v
decltype(std::declval< UnwrapIndirect_t< typename L::template ValueType_t< Seq > >>()==std::declval< UnwrapIndirect_t< typename R::template ValueType_t< Seq > >>()) ComparableDetector
SelectWrapper(const QSqlDatabase &db, const CachedFieldsData &data)
void operator()(const HeadArg &arg, const TailArgs &... tail) const
QString GetFieldName() const
auto operator()(Seq &t, InsertAction action=InsertAction::Default) const
QSet< QString > AdditionalTables() const
QSet< QString > AdditionalTables() const
auto HandleExprTree(const ExprTree< ExprType::ConstTrue > &, int lastId=0)
constexpr bool IsDetected_v
QString ToSql(ToSqlState< ObjT > &state) const
QStringList QualifiedFields_
typename detail::IsDetected< Type, void, Op, Args... >::type IsDetected_t
QStringList operator()() const
std::function< void(Seq)> Deleter_
AssignList(const L &l, const R &r)
QSet< QString > AdditionalTables() const
QString AdaptCreateTable(const CachedFieldsData &data)
static constexpr bool HasAutogen_
constexpr auto ConstTrueTree_v
QVariantMap BoundMembers_
typename boost::fusion::result_of::value_at< Seq, Idx >::type ValueAt_t
MemberPtrType_t< Ptr > ValueType_t
detail::SelectWrapper< T, detail::SelectBehaviour::One > SelectOne
detail::AdaptDelete< T > Delete
ObjectInfo< T > Adapt(const QSqlDatabase &db)
DeleteByFieldsWrapper(const QSqlDatabase &db, const CachedFieldsData &data)
QList< QString > BoundFields_
std::enable_if_t< B > operator()(const T &seq)
CachedFieldsData BuildCachedFieldsData(const QString &table)
detail::AdaptInsert< T > Insert
const QString InsertSuffix_
auto MakeIndexedQueryHandler(detail::MemberPtrs< Ptrs... >, std::index_sequence< Idxs... >)
Type
Describes the various types of XDG .desktop files.
typename boost::fusion::result_of::value_at_c< Seq, Idx >::type ValueAtC_t
AdaptDelete(const QSqlDatabase &db, const CachedFieldsData &data, std::enable_if_t< B > *=nullptr)
QList< QString > GetTypes(std::index_sequence< Indices... >)
QSet< QString > AdditionalTables() const
QStringList GetConstraintsStringList(Constraints< Args... >, const CachedFieldsData &data)
QStringList BuildFieldNames()
A proper void type, akin to unit (or ()) type in functional languages.
constexpr auto SeqIndices
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
const QSqlQuery_ptr & GetQueryPtr() const
constexpr size_t FieldIndex()
auto MakeInserter(const CachedFieldsData &data, const QSqlQuery_ptr &insertQuery, bool bindPrimaryKey)
QList< QString > BoundFields_
detail::AdaptUpdate< T > Update
QString ToSql(ToSqlState< T > &state) const
constexpr auto HasAutogenPKey()
typename detail::DecomposeMemberPtr< decltype(Ptr)>::Value_t MemberPtrType_t
static constexpr auto Index()
QString MorphFieldName(QString str)
std::conditional_t< std::is_same_v< detail::RetTypeRaw_t< F >, detail::ReturnsVoid >, void, detail::RetTypeRaw_t< F > > RetType_t
auto operator>(const L &left, const R &right)
typename std::conditional_t< IsIndirect< T > {}, T, WrapDirect< T > >::value_type UnwrapIndirect_t
QSqlQuery RunTextQuery(const QSqlDatabase &db, const QString &text)
Runs the given query text on the given db.
auto operator<(const L &left, const R &right)
constexpr bool IsRelational(ExprType type)
decltype(new T { std::declval< Args >()... }) AggregateDetector_t
auto Map(Container &&c, F f)
QString ToSql(ToSqlState< T > &) const
std::enable_if_t< B > operator()(const Seq &seq)
constexpr detail::MemberPtrs< Ptrs... > fields
T operator()(const QVariant &var) const
decltype(std::declval< U >().FieldNameMorpher(QString {})) MorpherDetector
ExprTree(const L &l, const R &r)
auto operator,(const AssignList< OL, OR > &tail)
constexpr auto AsLeafData(const T &node)
auto operator &&(const L &left, const R &right)
static UTIL_DB_API void DumpError(const QSqlError &error)
Dumps the error to the qWarning() stream.
ExprTree< Type, L, R > MakeExprTree(const L &left, const R &right)
std::shared_ptr< QSqlQuery > QSqlQuery_ptr
QueryException(const std::string &str, const QSqlQuery_ptr &q)
QString ToSql(ToSqlState< T > &state) const
typename AsTypelist< T >::Result_t AsTypelist_t
void operator()(const ExprTree< Type, L, R > &tree) const
QString GetInsertPrefix(InsertAction action)
boost::mpl::int_< FindPKey< Seq >::result_type::value > FindPKeyDetector
QVariant operator()(const T &t) const
AdaptUpdate(const QSqlDatabase &db, const CachedFieldsData &data)
constexpr auto AreComparableTypes
ValueAtC_t< T, Idx > ValueType_t
FieldsUnpacker< TailT... > Tail_t