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> 81 :
std::runtime_error (str)
109 if constexpr (IsDetected_v<MorpherDetector, T>)
110 return T::FieldNameMorpher (str);
113 if (str.endsWith (
'_'))
119 template<
typename Seq,
int Idx>
124 return MorphFieldName<Seq> (boost::fusion::extension::struct_member_name<Seq, Idx>::call ());
129 constexpr
auto SeqSize = boost::fusion::result_of::size<S>::type::value;
132 constexpr
auto SeqIndices = std::make_index_sequence<SeqSize<S>> {};
139 return Run (SeqIndices<S>);
142 template<
size_t... Vals>
143 QStringList Run (std::index_sequence<Vals...>)
const 149 template<
typename Seq,
int Idx>
161 static constexpr
auto Ptr ()
169 return &boost::fusion::at_c<Idx> (
Obj_);
173 template<auto Ptr,
size_t Idx = 0>
178 if constexpr (Idx == SeqSize<S>)
179 throw std::runtime_error {
"wut, no such field?" };
184 if constexpr (std::is_same_v<decltype (direct), decltype (indexed)>)
186 if (indexed == direct)
190 return FieldIndex<Ptr, Idx + 1> ();
195 template<
typename ImplFactory,
typename T,
typename =
void>
202 else if constexpr (std::is_same_v<T, double>)
204 else if constexpr (std::is_same_v<T, QString> || std::is_same_v<T, QDateTime>)
206 else if constexpr (std::is_same_v<T, QByteArray>)
209 static_assert (std::is_same_v<T, struct Dummy>,
"Unsupported type");
213 template<
typename ImplFactory,
typename T>
219 template<
typename ImplFactory,
typename T>
225 template<
typename ImplFactory,
typename T,
typename... Tags>
231 template<
typename ImplFactory,
typename... Tags>
234 QString
operator() ()
const {
return ImplFactory::TypeLits::IntAutoincrement; }
237 template<
typename ImplFactory, auto Ptr>
243 constexpr
auto idx = detail::FieldIndex<Ptr> ();
249 template<
typename T,
typename =
void>
254 if constexpr (std::is_same_v<T, QDateTime>)
255 return t.toString (Qt::ISODate);
256 else if constexpr (std::is_enum_v<T>)
257 return static_cast<qint64
> (t);
265 template<
typename T,
typename =
void>
270 if constexpr (std::is_same_v<T, QDateTime>)
271 return QDateTime::fromString (var.toString (), Qt::ISODate);
272 else if constexpr (std::is_enum_v<T>)
273 return static_cast<T
> (var.value<qint64> ());
277 return var.value<T> ();
286 template<
typename U,
typename... Tags>
298 return [data, insertQuery, bindPrimaryKey] (
const T& t)
301 [&] (
auto pos,
const auto& elem)
303 using Elem = std::decay_t<decltype (elem)>;
309 if (!insertQuery->exec ())
312 throw QueryException (
"insert query execution failed", insertQuery);
317 template<
typename Seq,
int Idx>
318 using ValueAtC_t =
typename boost::fusion::result_of::value_at_c<Seq, Idx>::type;
320 template<
typename Seq,
typename Idx>
321 using ValueAt_t =
typename boost::fusion::result_of::value_at<Seq, Idx>::type;
323 template<
typename Seq,
typename MemberIdx = boost::mpl::
int_<0>>
326 static_assert ((boost::fusion::result_of::size<Seq>::value) != (MemberIdx::value),
327 "Primary key not found");
342 template<
typename Seq>
345 template<
typename Seq>
346 constexpr
auto HasPKey = IsDetected_v<FindPKeyDetector, Seq>;
348 template<
typename Seq>
351 if constexpr (HasPKey<Seq>)
361 const auto& qualified =
Util::Map (
fields, [&table] (
const QString& field) {
return table +
"." + field; });
362 const auto& boundFields =
Util::Map (
fields, [] (
const QString& str) {
return ':' + str; });
364 return { table,
fields, qualified, boundFields };
370 static CachedFieldsData result = BuildCachedFieldsData<T> (T::ClassName ());
374 template<
typename Seq>
377 const QSqlDatabase DB_;
380 constexpr
static bool HasAutogen_ = HasAutogenPKey<Seq> ();
384 template<
typename ImplFactory>
386 : Data_ { RemovePKey (data) }
387 , QueryBuilder_ { factory.MakeInsertQueryBuilder (db, Data_) }
393 return Run<true> (t, action);
398 return Run<false> (t, action);
401 template<
bool UpdatePKey,
typename Val>
404 const auto query = QueryBuilder_->GetQuery (action);
406 MakeInserter<Seq> (Data_, query, !HasAutogen_) (t);
408 if constexpr (HasAutogen_)
413 if constexpr (UpdatePKey)
414 boost::fusion::at_c<index> (t) = lastId;
420 static CachedFieldsData RemovePKey (CachedFieldsData data)
422 if constexpr (HasAutogen_)
424 constexpr
auto index = FindPKey<Seq>::result_type::value;
425 data.Fields_.removeAt (index);
426 data.BoundFields_.removeAt (index);
432 template<
typename Seq,
bool HasPKey = HasPKey<Seq>>
437 template<
bool B = HasPKey>
443 const auto& del =
"DELETE FROM " + data.
Table_ +
444 " WHERE " + data.
Fields_.at (index) +
" = " + boundName +
";";
446 const auto deleteQuery = std::make_shared<QSqlQuery> (db);
447 deleteQuery->prepare (del);
449 Deleter_ = [deleteQuery, boundName] (
const Seq& t)
452 deleteQuery->bindValue (boundName,
ToVariantF (boost::fusion::at_c<index> (t)));
453 if (!deleteQuery->exec ())
454 throw QueryException (
"delete query execution failed", deleteQuery);
458 template<
bool B = HasPKey>
463 template<
bool B = HasPKey>
470 template<
typename T,
typename... Args>
473 template<
typename T,
size_t... Indices>
481 const auto dummy = std::initializer_list<int>
490 template<
int HeadT,
int... TailT>
504 template<
typename FieldsUnpacker,
typename HeadArg,
typename... TailArgs>
510 void operator() (
const HeadArg& arg,
const TailArgs&... tail)
const 518 template<
typename FieldsUnpacker,
typename HeadArg>
572 return "invalid type";
606 template<
typename Seq,
typename L,
typename R>
610 template<
typename Seq,
typename L,
typename R>
611 constexpr
auto AreComparableTypes = IsDetected_v<ComparableDetector, Seq, L, R> || IsDetected_v<ComparableDetector, Seq, R, L>;
613 template<
typename Seq,
typename L,
typename R,
typename =
void>
616 template<
typename Seq,
typename L,
typename R>
619 template<ExprType Type,
typename Seq,
typename L,
typename R,
typename =
void>
622 template<ExprType Type,
typename Seq,
typename L,
typename R>
625 template<ExprType Type,
typename L =
void,
typename R =
void>
631 template<ExprType Type,
typename L,
typename R>
634 template<
typename L,
typename R>
650 return Left_.GetFieldName () +
" = " + Right_.ToSql (state);
652 return Left_.ToSql (state) +
", " + Right_.ToSql (state);
655 template<
typename OL,
typename OR>
662 template<ExprType Type,
typename L,
typename R>
678 "Incompatible types passed to a relational operator.");
680 return Left_.ToSql (state) +
" " +
TypeToSql (
Type) +
" " + Right_.ToSql (state);
686 return Left_.template AdditionalTables<T> () + Right_.template AdditionalTables<T> ();
700 static_assert (Idx < boost::fusion::result_of::size<T>::type::value,
"Index out of bounds.");
711 template<
auto... Ptr>
730 constexpr
auto idx = FieldIndex<Ptr> ();
738 if constexpr (std::is_same_v<Seq, T>)
741 return { Seq::ClassName () };
745 auto operator= (
const R&)
const;
761 template<
typename ObjT>
764 const auto& name =
":bound_" + QString::number (++state.
LastID_);
797 template<ExprType Type,
typename L,
typename R>
800 return { left, right };
803 template<
typename L,
typename R>
806 template<
typename L,
typename R>
809 template<
typename L,
typename R,
typename = EnableRelOp_t<L, R>>
812 if constexpr (AllTrees_v<L, R>)
813 return MakeExprTree<ExprType::Less> (left, right);
818 template<
typename L,
typename R,
typename = EnableRelOp_t<L, R>>
821 if constexpr (AllTrees_v<L, R>)
822 return MakeExprTree<ExprType::Greater> (left, right);
827 template<
typename L,
typename R,
typename = EnableRelOp_t<L, R>>
830 if constexpr (AllTrees_v<L, R>)
831 return MakeExprTree<ExprType::Equal> (left, right);
836 template<
typename L,
typename R,
typename = EnableRelOp_t<L, R>>
839 if constexpr (AllTrees_v<L, R>)
840 return MakeExprTree<ExprType::And> (left, right);
848 return std::tuple { QString {},
Void {}, lastId };
851 template<
typename Seq,
typename Tree,
852 typename = decltype (std::declval<Tree> ().ToSql (std::declval<ToSqlState<Seq>&> ()))>
857 const auto& sql = tree.ToSql (state);
862 [state] (QSqlQuery& query)
864 for (
const auto& pair :
Stlize (state.BoundMembers_))
865 query.bindValue (pair.first, pair.second);
876 template<AggregateFunction>
885 static constexpr
pos<0> _0 = {};
886 static constexpr pos<1> _1 = {};
887 static constexpr pos<2> _2 = {};
888 static constexpr pos<3> _3 = {};
889 static constexpr pos<4> _4 = {};
894 template<
auto... Ptrs>
902 template<
auto... Ptrs,
size_t... Idxs>
905 return [] (
const QSqlQuery& q)
907 if constexpr (
sizeof... (Ptrs) == 1)
914 template<
auto... Ptrs>
917 return { BuildCachedFieldsData<MemberPtrStruct_t<Ptrs>> ().QualifiedFields_.value (FieldIndex<Ptrs> ())... };
922 template<
typename T, SelectBehaviour SelectBehaviour>
925 const QSqlDatabase DB_;
928 struct SelectWhole {};
941 template<
typename Single>
944 if constexpr (
IsExprTree<std::decay_t<Single>> {})
945 return (*
this) (SelectWhole {}, std::forward<Single> (single));
950 template<
typename Selector, ExprType Type,
typename L,
typename R>
953 const auto& [where, binder, _] = HandleExprTree<T> (tree);
955 const auto& [
fields, initializer, postproc] = HandleSelector (std::forward<Selector> (selector));
956 return postproc (Select (
fields, BuildFromClause (tree), where, binder, initializer));
959 template<
typename Binder,
typename Initializer>
960 auto Select (
const QString&
fields,
const QString& from, QString where,
961 Binder&& binder, Initializer&& initializer)
const 963 if (!where.isEmpty ())
964 where.prepend (
" WHERE ");
966 const auto& queryStr =
"SELECT " +
fields +
970 QSqlQuery query { DB_ };
971 query.prepare (queryStr);
972 if constexpr (!std::is_same_v<
Void, std::decay_t<Binder>>)
976 throw
QueryException ("fetch query execution failed",
std::make_shared<QSqlQuery> (query));
981 while (query.next ())
982 result << initializer (query);
987 using RetType_t = boost::optional<std::result_of_t<Initializer (QSqlQuery)>>;
988 return query.next () ?
994 template<ExprType Type,
typename L,
typename R>
995 QString BuildFromClause (
const ExprTree<Type, L, R>& tree)
const 999 const auto& additionalTables = Util::MapAs<QList> (tree.template AdditionalTables<T> (),
1000 [] (
const QString& table) {
return ", " + table; });
1001 return Cached_.
Table_ + additionalTables.join (QString {});
1007 auto HandleSelector (SelectWhole)
const 1012 [] (
const QSqlQuery& q) {
return InitializeFromQuery<T> (q, SeqIndices<T>); },
1018 auto HandleSelector (sph::pos<Idx>)
const 1023 [] (
const QSqlQuery& q) {
return FromVariant<UnwrapIndirect_t<ValueAtC_t<T, Idx>>> {} (q.value (0)); },
1028 template<
auto... Ptrs>
1029 auto HandleSelector (MemberPtrs<Ptrs...> ptrs)
const 1039 template<AggregateFunction Fun>
1040 auto HandleSelector (AggregateType<Fun>)
const 1045 QString {
"count(1)" },
1046 [] (
const QSqlQuery& q) {
return q.value (0).toLongLong (); },
1052 template<
typename T>
1055 const QSqlDatabase DB_;
1064 template<ExprType Type,
typename L,
typename R>
1067 const auto& [where, binder, _] = HandleExprTree<T> (tree);
1070 const auto& selectAll =
"DELETE FROM " + Cached_.
Table_ +
1071 " WHERE " + where +
";";
1073 QSqlQuery query { DB_ };
1074 query.prepare (selectAll);
1080 template<
typename T,
bool HasPKey = HasPKey<T>>
1083 const QSqlDatabase DB_;
1086 std::function<void (T)> Updater_;
1094 const auto index = FindPKey<T>::result_type::value;
1099 const auto& fieldName = removedFields.takeAt (index);
1100 const auto& boundName = removedBoundFields.takeAt (index);
1102 const auto& statements =
Util::ZipWith (removedFields, removedBoundFields,
1103 [] (
const QString& s1,
const QString& s2) {
return s1 +
" = " + s2; });
1105 const auto& update =
"UPDATE " + data.Table_ +
1106 " SET " + statements.join (
", ") +
1107 " WHERE " + fieldName +
" = " + boundName +
";";
1109 const auto updateQuery = std::make_shared<QSqlQuery> (db);
1110 updateQuery->prepare (update);
1111 Updater_ = MakeInserter<T> (data, updateQuery,
true);
1115 template<
bool B = HasPKey>
1121 template<
typename SL,
typename SR, ExprType WType,
typename WL,
typename WR>
1124 const auto& [setClause, setBinder, setLast] = HandleExprTree<T> (
set);
1125 const auto& [whereClause, whereBinder, _] = HandleExprTree<T> (where, setLast);
1127 const auto& update =
"UPDATE " + Cached_.
Table_ +
1128 " SET " + setClause +
1129 " WHERE " + whereClause;
1131 QSqlQuery query { DB_ };
1132 query.prepare (update);
1134 whereBinder (query);
1139 template<
typename T>
1142 template<
typename T>
1145 template<
typename T>
1148 template<
int... Fields>
1153 return "UNIQUE (" + QStringList { data.
Fields_.value (Fields)... }.join (
", ") +
")";
1157 template<
int... Fields>
1162 return "PRIMARY KEY (" + QStringList { data.
Fields_.value (Fields)... }.join (
", ") +
")";
1166 template<
typename... Args>
1172 template<
typename ImplFactory,
typename T,
size_t... Indices>
1178 template<
typename ImplFactory,
typename T>
1181 const auto& types = GetTypes<ImplFactory, T> (SeqIndices<T>);
1184 const auto& constraintsStr = constraints.isEmpty () ?
1186 (
", " + constraints.join (
", "));
1189 [] (
const QString& type,
const QString& field) { return field +
" " + type; });
1190 return "CREATE TABLE " +
1193 statements.join (
", ") +
1199 template<
auto... Ptrs>
1202 return { { detail::BuildCachedFieldsData<MemberPtrStruct_t<Ptrs>> ().
Fields_.value (detail::FieldIndex<Ptrs> ())... } };
1205 template<
typename Seq>
1208 static_assert (detail::HasPKey<Seq>,
"Sequence does not have any primary keys");
1212 template<
typename T>
1227 template<
typename T,
typename ImplFactory = detail::SQLite::ImplFactory>
1230 const auto& cachedData = detail::BuildCachedFieldsData<T> ();
1232 if (db.record (cachedData.Table_).isEmpty ())
1233 RunTextQuery (db, detail::AdaptCreateTable<ImplFactory, T> (cachedData));
1235 ImplFactory factory;
1239 { db, cachedData, factory },
1248 template<
typename T>
1251 template<
typename T,
typename ImplFactory = SQLiteImplFactory>
1254 return std::make_shared<ObjectInfo<T>> (Adapt<T, ImplFactory> (db));
std::enable_if_t< AnyOf< IsExprTree, L, R > > EnableRelOp_t
detail::DeleteByFieldsWrapper< T > DeleteBy
constexpr detail::AggregateType< detail::AggregateFunction::Count > count
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.
QString operator()() const
detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, boost::mpl::int_< Idx > > pos
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
Util::IsDetected_t< Constraints<>, ConstraintsDetector, T > ConstraintsType
ObjectInfo< T > Adapt(const QSqlDatabase &db)
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)
ObjectInfo(const ObjectInfo< T > &)=delete
QString TypeToSql(ExprType type)
typename detail::DecomposeMemberPtr< decltype(Ptr)>::StructType_t MemberPtrStruct_t
auto operator==(const L &left, const R &right)
QList< QString > GetTypes(std::index_sequence< Indices... >)
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
constexpr auto ConstTrueTree_v
AdaptInsert(const QSqlDatabase &db, CachedFieldsData data, ImplFactory &&factory)
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
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
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
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)
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
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)
virtual ~QueryException() noexcept
QueryException(const std::string &str, const QSqlQuery_ptr &q)
QString AdaptCreateTable(const CachedFieldsData &data)
static struct LeechCraft::Util::oral::InsertAction::DefaultTag Default
QString ToSql(ToSqlState< T > &state) const
typename AsTypelist< T >::Result_t AsTypelist_t
void operator()(const ExprTree< Type, L, R > &tree) const
boost::mpl::int_< FindPKey< Seq >::result_type::value > FindPKeyDetector
QVariant operator()(const T &t) const
AdaptUpdate(const QSqlDatabase &db, const CachedFieldsData &data)
std::unique_ptr< IInsertQueryBuilder > IInsertQueryBuilder_ptr
constexpr auto AreComparableTypes
ValueAtC_t< T, Idx > ValueType_t
std::shared_ptr< QSqlQuery > QSqlQuery_ptr
FieldsUnpacker< TailT... > Tail_t