LeechCraft 0.6.70-14794-g33744ae6ce
Modular cross-platform feature rich live environment.
Loading...
Searching...
No Matches
oral.h
Go to the documentation of this file.
1/**********************************************************************
2 * LeechCraft - modular cross-platform feature rich internet client.
3 * Copyright (C) 2006-2014 Georg Rudoy
4 *
5 * Distributed under the Boost Software License, Version 1.0.
6 * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
7 **********************************************************************/
8
9#pragma once
10
11#include <stdexcept>
12#include <type_traits>
13#include <memory>
14#include <optional>
15#include <boost/fusion/include/for_each.hpp>
16#include <boost/fusion/include/fold.hpp>
17#include <boost/fusion/include/filter_if.hpp>
18#include <boost/fusion/container/vector.hpp>
19#include <boost/fusion/include/vector.hpp>
20#include <boost/fusion/include/transform.hpp>
21#include <boost/fusion/include/zip.hpp>
22#include <boost/fusion/container/generation/make_vector.hpp>
23#include <QStringList>
24#include <QDateTime>
25#include <QPair>
26#include <QSqlQuery>
27#include <QSqlRecord>
28#include <QVariant>
29#include <QDateTime>
30#include <QtDebug>
31#include <util/sll/qtutil.h>
32#include <util/sll/prelude.h>
33#include <util/sll/typelist.h>
34#include <util/sll/typelevel.h>
35#include <util/sll/typegetter.h>
36#include <util/sll/detector.h>
38#include <util/sll/void.h>
39#include <util/db/dblock.h>
40#include <util/db/util.h>
41#include "oraltypes.h"
42#include "oraldetailfwd.h"
43#include "impldefs.h"
44#include "sqliteimpl.h"
45
46namespace LC
47{
48namespace Util
49{
50namespace oral
51{
52 using QSqlQuery_ptr = std::shared_ptr<QSqlQuery>;
53
54 class QueryException : public std::runtime_error
55 {
56 const QSqlQuery_ptr Query_;
57 public:
58 QueryException (const std::string& str, const QSqlQuery_ptr& q)
59 : std::runtime_error (str)
60 , Query_ (q)
61 {
62 }
63
64 ~QueryException () noexcept = default;
65
66 const QSqlQuery_ptr& GetQueryPtr () const
67 {
68 return Query_;
69 }
70
71 const QSqlQuery& GetQuery () const
72 {
73 return *Query_;
74 }
75 };
76
77 namespace detail
78 {
79 template<typename U>
80 using MorpherDetector = decltype (std::declval<U> ().FieldNameMorpher (QString {}));
81
82 template<typename T>
83 QString MorphFieldName (QString str) noexcept
84 {
85 if constexpr (IsDetected_v<MorpherDetector, T>)
86 return T::FieldNameMorpher (str);
87 else
88 {
89 if (str.endsWith ('_'))
90 str.chop (1);
91 return str;
92 }
93 }
94
95 template<typename Seq, int Idx>
96 QString GetFieldName () noexcept
97 {
98 return MorphFieldName<Seq> (boost::fusion::extension::struct_member_name<Seq, Idx>::call ());
99 }
100
101 template<typename S>
102 constexpr auto SeqSize = boost::fusion::result_of::size<S>::type::value;
103
104 template<typename S>
105 constexpr auto SeqIndices = std::make_index_sequence<SeqSize<S>> {};
106
107 template<typename S>
109 {
110 QStringList operator() () const noexcept
111 {
112 return Run (SeqIndices<S>);
113 }
114 private:
115 template<size_t... Vals>
116 QStringList Run (std::index_sequence<Vals...>) const noexcept
117 {
118 return { GetFieldName<S, Vals> ()... };
119 }
120 };
121
122 template<typename S>
124 {
125 inline static S Obj_ {};
126
127 template<auto P>
128 static constexpr auto Ptr () noexcept
129 {
130 return &(Obj_.*P);
131 }
132
133 template<int Idx>
134 static constexpr auto Index () noexcept
135 {
136 return &boost::fusion::at_c<Idx> (Obj_);
137 }
138 };
139
140 template<auto Ptr, size_t Idx = 0>
141 constexpr size_t FieldIndex () noexcept
142 {
143 using S = MemberPtrStruct_t<Ptr>;
144
145 if constexpr (Idx == SeqSize<S>)
146 return -1;
147 else
148 {
149 constexpr auto direct = AddressOf<S>::template Ptr<Ptr> ();
150 constexpr auto indexed = AddressOf<S>::template Index<Idx> ();
151 if constexpr (std::is_same_v<decltype (direct), decltype (indexed)>)
152 {
153 if (indexed == direct)
154 return Idx;
155 }
156
157 return FieldIndex<Ptr, Idx + 1> ();
158 }
159 }
160
161 template<auto Ptr>
162 QString GetFieldNamePtr () noexcept
163 {
164 using S = MemberPtrStruct_t<Ptr>;
165 return GetFieldName<S, FieldIndex<Ptr> ()> ();
166 }
167
168 template<auto Ptr>
169 QString GetQualifiedFieldNamePtr () noexcept
170 {
171 using S = MemberPtrStruct_t<Ptr>;
172 return S::ClassName () + "." + GetFieldName<S, FieldIndex<Ptr> ()> ();
173 }
174
175 template<typename T>
176 using TypeNameDetector = decltype (T::TypeName);
177
178 template<typename T>
179 constexpr bool TypeNameCustomized = IsDetected_v<TypeNameDetector, T>;
180
181 template<typename T>
182 using BaseTypeDetector = typename T::BaseType;
183
184 template<typename T>
185 constexpr bool BaseTypeCustomized = IsDetected_v<BaseTypeDetector, T>;
186 }
187
188 template<typename ImplFactory, typename T, typename = void>
190 {
191 QString operator() () const noexcept
192 {
193 if constexpr (HasType<T> (Typelist<int, qlonglong, qulonglong, bool> {}) || std::is_enum_v<T>)
194 return "INTEGER";
195 else if constexpr (std::is_same_v<T, double>)
196 return "REAL";
197 else if constexpr (std::is_same_v<T, QString> || std::is_same_v<T, QDateTime> || std::is_same_v<T, QUrl>)
198 return "TEXT";
199 else if constexpr (std::is_same_v<T, QByteArray>)
200 return ImplFactory::TypeLits::Binary;
201 else if constexpr (detail::TypeNameCustomized<T>)
202 return T::TypeName;
203 else if constexpr (detail::BaseTypeCustomized<T>)
205 else
206 static_assert (std::is_same_v<T, struct Dummy>, "Unsupported type");
207 }
208 };
209
210 template<typename ImplFactory, typename T>
211 struct Type2Name<ImplFactory, Unique<T>>
212 {
213 QString operator() () const noexcept { return Type2Name<ImplFactory, T> () () + " UNIQUE"; }
214 };
215
216 template<typename ImplFactory, typename T>
217 struct Type2Name<ImplFactory, NotNull<T>>
218 {
219 QString operator() () const noexcept { return Type2Name<ImplFactory, T> () () + " NOT NULL"; }
220 };
221
222 template<typename ImplFactory, typename T, typename... Tags>
223 struct Type2Name<ImplFactory, PKey<T, Tags...>>
224 {
225 QString operator() () const noexcept { return Type2Name<ImplFactory, T> () () + " PRIMARY KEY"; }
226 };
227
228 template<typename ImplFactory, typename... Tags>
229 struct Type2Name<ImplFactory, PKey<int, Tags...>>
230 {
231 QString operator() () const noexcept { return ImplFactory::TypeLits::IntAutoincrement; }
232 };
233
234 template<typename ImplFactory, auto Ptr>
235 struct Type2Name<ImplFactory, References<Ptr>>
236 {
237 QString operator() () const noexcept
238 {
239 const auto& className = MemberPtrStruct_t<Ptr>::ClassName ();
241 " REFERENCES " + className + " (" + detail::GetFieldNamePtr<Ptr> () + ") ON DELETE CASCADE";
242 }
243 };
244
245 template<typename T, typename = void>
247 {
248 QVariant operator() (const T& t) const noexcept
249 {
250 if constexpr (std::is_same_v<T, QDateTime>)
251 return t.toString (Qt::ISODate);
252 else if constexpr (std::is_enum_v<T>)
253 return static_cast<qint64> (t);
254 else if constexpr (IsIndirect<T> {})
256 else if constexpr (detail::TypeNameCustomized<T>)
257 return t.ToVariant ();
258 else if constexpr (detail::BaseTypeCustomized<T>)
259 return ToVariant<typename T::BaseType> {} (t.ToBaseType ());
260 else
261 return t;
262 }
263 };
264
265 template<typename T, typename = void>
267 {
268 T operator() (const QVariant& var) const noexcept
269 {
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> ());
274 else if constexpr (IsIndirect<T> {})
276 else if constexpr (detail::TypeNameCustomized<T>)
277 return T::FromVariant (var);
278 else if constexpr (detail::BaseTypeCustomized<T>)
279 return T::FromBaseType (FromVariant<typename T::BaseType> {} (var));
280 else
281 return var.value<T> ();
282 }
283 };
284
285 namespace detail
286 {
287 template<typename T>
288 struct IsPKey : std::false_type {};
289
290 template<typename U, typename... Tags>
291 struct IsPKey<PKey<U, Tags...>> : std::true_type {};
292
293 template<typename T>
294 QVariant ToVariantF (const T& t) noexcept
295 {
296 return ToVariant<T> {} (t);
297 }
298
299 template<typename T>
300 auto MakeInserter (const CachedFieldsData& data, const QSqlQuery_ptr& insertQuery, bool bindPrimaryKey) noexcept
301 {
302 return [data, insertQuery, bindPrimaryKey] (const T& t)
303 {
304 boost::fusion::fold (t, data.BoundFields_.begin (),
305 [&] (auto pos, const auto& elem)
306 {
307 using Elem = std::decay_t<decltype (elem)>;
308 if (bindPrimaryKey || !IsPKey<Elem>::value)
309 insertQuery->bindValue (*pos++, ToVariantF (elem));
310 return pos;
311 });
312
313 if (!insertQuery->exec ())
314 {
315 DBLock::DumpError (*insertQuery);
316 throw QueryException ("insert query execution failed", insertQuery);
317 }
318 };
319 }
320
321 template<typename Seq, int Idx>
322 using ValueAtC_t = typename boost::fusion::result_of::value_at_c<Seq, Idx>::type;
323
324 template<typename Seq, typename Idx>
325 using ValueAt_t = typename boost::fusion::result_of::value_at<Seq, Idx>::type;
326
327 template<typename Seq, typename MemberIdx = boost::mpl::int_<0>>
328 struct FindPKey
329 {
330 static_assert ((boost::fusion::result_of::size<Seq>::value) != (MemberIdx::value),
331 "Primary key not found");
332
333 template<typename T>
334 struct Lazy
335 {
336 using type = T;
337 };
338
339 using result_type = typename std::conditional_t<
343 >::type;
344 };
345
346 template<typename Seq>
347 using FindPKeyDetector = boost::mpl::int_<FindPKey<Seq>::result_type::value>;
348
349 template<typename Seq>
350 constexpr auto HasPKey = IsDetected_v<FindPKeyDetector, Seq>;
351
352 template<typename Seq>
353 constexpr auto HasAutogenPKey () noexcept
354 {
355 if constexpr (HasPKey<Seq>)
356 return !HasType<NoAutogen> (AsTypelist_t<ValueAtC_t<Seq, FindPKey<Seq>::result_type::value>> {});
357 else
358 return false;
359 }
360
361 template<typename T>
362 CachedFieldsData BuildCachedFieldsData (const QString& table) noexcept
363 {
364 const auto& fields = detail::GetFieldsNames<T> {} ();
365 const auto& qualified = Util::Map (fields, [&table] (const QString& field) { return table + "." + field; });
366 const auto& boundFields = Util::Map (fields, [] (const QString& str) { return ':' + str; });
367
368 return { table, fields, qualified, boundFields };
369 }
370
371 template<typename T>
373 {
374 static CachedFieldsData result = BuildCachedFieldsData<T> (T::ClassName ());
375 return result;
376 }
377
378 template<typename Seq>
380 {
381 const QSqlDatabase DB_;
382 const CachedFieldsData Data_;
383
384 constexpr static bool HasAutogen_ = HasAutogenPKey<Seq> ();
385
386 IInsertQueryBuilder_ptr QueryBuilder_;
387 public:
388 template<typename ImplFactory>
389 AdaptInsert (const QSqlDatabase& db, CachedFieldsData data, ImplFactory&& factory) noexcept
390 : Data_ { RemovePKey (data) }
391 , QueryBuilder_ { factory.MakeInsertQueryBuilder (db, Data_) }
392 {
393 }
394
395 auto operator() (Seq& t, InsertAction action = InsertAction::Default) const
396 {
397 return Run<true> (t, action);
398 }
399
400 auto operator() (const Seq& t, InsertAction action = InsertAction::Default) const
401 {
402 return Run<false> (t, action);
403 }
404 private:
405 template<bool UpdatePKey, typename Val>
406 auto Run (Val&& t, InsertAction action) const
407 {
408 const auto query = QueryBuilder_->GetQuery (action);
409
410 MakeInserter<Seq> (Data_, query, !HasAutogen_) (t);
411
412 if constexpr (HasAutogen_)
413 {
414 constexpr auto index = FindPKey<Seq>::result_type::value;
415
416 const auto& lastId = FromVariant<ValueAtC_t<Seq, index>> {} (query->lastInsertId ());
417 if constexpr (UpdatePKey)
418 boost::fusion::at_c<index> (t) = lastId;
419 else
420 return lastId;
421 }
422 }
423
424 static CachedFieldsData RemovePKey (CachedFieldsData data) noexcept
425 {
426 if constexpr (HasAutogen_)
427 {
428 constexpr auto index = FindPKey<Seq>::result_type::value;
429 data.Fields_.removeAt (index);
430 data.BoundFields_.removeAt (index);
431 }
432 return data;
433 }
434 };
435
436 template<typename Seq, bool HasPKey = HasPKey<Seq>>
438 {
439 std::function<void (Seq)> Deleter_;
440 public:
441 template<bool B = HasPKey>
442 AdaptDelete (const QSqlDatabase& db, const CachedFieldsData& data, std::enable_if_t<B>* = nullptr) noexcept
443 {
444 const auto index = FindPKey<Seq>::result_type::value;
445
446 const auto& boundName = data.BoundFields_.at (index);
447 const auto& del = "DELETE FROM " + data.Table_ +
448 " WHERE " + data.Fields_.at (index) + " = " + boundName;
449
450 const auto deleteQuery = std::make_shared<QSqlQuery> (db);
451 deleteQuery->prepare (del);
452
453 Deleter_ = [deleteQuery, boundName] (const Seq& t)
454 {
455 constexpr auto index = FindPKey<Seq>::result_type::value;
456 deleteQuery->bindValue (boundName, ToVariantF (boost::fusion::at_c<index> (t)));
457 if (!deleteQuery->exec ())
458 throw QueryException ("delete query execution failed", deleteQuery);
459 };
460 }
461
462 template<bool B = HasPKey>
463 AdaptDelete (const QSqlDatabase&, const CachedFieldsData&, std::enable_if_t<!B>* = nullptr) noexcept
464 {
465 }
466
467 template<bool B = HasPKey>
468 std::enable_if_t<B> operator() (const Seq& seq)
469 {
470 Deleter_ (seq);
471 }
472 };
473
474 template<typename T, typename... Args>
475 using AggregateDetector_t = decltype (new T { std::declval<Args> ()... });
476
477 template<typename T, size_t... Indices>
478 T InitializeFromQuery (const QSqlQuery& q, std::index_sequence<Indices...>, int startIdx) noexcept
479 {
480 if constexpr (IsDetected_v<AggregateDetector_t, T, ValueAtC_t<T, Indices>...>)
481 return T { FromVariant<ValueAtC_t<T, Indices>> {} (q.value (startIdx + Indices))... };
482 else
483 {
484 T t;
485 const auto dummy = std::initializer_list<int>
486 {
487 (static_cast<void> (boost::fusion::at_c<Indices> (t) = FromVariant<ValueAtC_t<T, Indices>> {} (q.value (startIdx + Indices))), 0)...
488 };
489 Q_UNUSED (dummy);
490 return t;
491 }
492 }
493
494 enum class ExprType
495 {
496 ConstTrue,
497
499 LeafData,
500
501 Greater,
502 Less,
503 Equal,
504 Geq,
505 Leq,
506 Neq,
507
508 Like,
509
510 And,
511 Or
512 };
513
514 inline QString TypeToSql (ExprType type) noexcept
515 {
516 switch (type)
517 {
519 return ">";
520 case ExprType::Less:
521 return "<";
522 case ExprType::Equal:
523 return "=";
524 case ExprType::Geq:
525 return ">=";
526 case ExprType::Leq:
527 return "<=";
528 case ExprType::Neq:
529 return "!=";
530 case ExprType::Like:
531 return "LIKE";
532 case ExprType::And:
533 return "AND";
534 case ExprType::Or:
535 return "OR";
536
540 return "invalid type";
541 }
542
544 }
545
546 constexpr bool IsRelational (ExprType type) noexcept
547 {
548 return type == ExprType::Greater ||
549 type == ExprType::Less ||
550 type == ExprType::Equal ||
551 type == ExprType::Geq ||
552 type == ExprType::Leq ||
553 type == ExprType::Neq ||
554 type == ExprType::Like;
555 }
556
557 template<typename T>
559 {
561 QVariantMap BoundMembers_;
562 };
563
564 template<typename T>
566 {
567 using value_type = T;
568 };
569
570 template<typename T>
571 using UnwrapIndirect_t = typename std::conditional_t<IsIndirect<T> {},
572 T,
573 WrapDirect<T>>::value_type;
574
575 template<typename Seq, typename L, typename R>
576 using ComparableDetector = decltype (std::declval<UnwrapIndirect_t<typename L::template ValueType_t<Seq>>> () ==
577 std::declval<UnwrapIndirect_t<typename R::template ValueType_t<Seq>>> ());
578
579 template<typename Seq, typename L, typename R>
580 constexpr auto AreComparableTypes = IsDetected_v<ComparableDetector, Seq, L, R> || IsDetected_v<ComparableDetector, Seq, R, L>;
581
582 template<typename Seq, typename L, typename R, typename = void>
583 struct RelationalTypesCheckerBase : std::false_type {};
584
585 template<typename Seq, typename L, typename R>
586 struct RelationalTypesCheckerBase<Seq, L, R, std::enable_if_t<AreComparableTypes<Seq, L, R>>> : std::true_type {};
587
588 template<ExprType Type, typename Seq, typename L, typename R, typename = void>
589 struct RelationalTypesChecker : std::true_type {};
590
591 template<ExprType Type, typename Seq, typename L, typename R>
592 struct RelationalTypesChecker<Type, Seq, L, R, std::enable_if_t<IsRelational (Type)>> : RelationalTypesCheckerBase<Seq, L, R> {};
593
594 template<ExprType Type, typename L = void, typename R = void>
595 class ExprTree;
596
597 template<typename T>
598 struct IsExprTree : std::false_type {};
599
600 template<ExprType Type, typename L, typename R>
601 struct IsExprTree<ExprTree<Type, L, R>> : std::true_type {};
602
603 template<typename L, typename R>
605 {
606 L Left_;
607 R Right_;
608 public:
609 AssignList (const L& l, const R& r) noexcept
610 : Left_ { l }
611 , Right_ { r }
612 {
613 }
614
615 template<typename T>
616 QString ToSql (ToSqlState<T>& state) const noexcept
617 {
618 if constexpr (IsExprTree<L> {})
619 return Left_.GetFieldName () + " = " + Right_.ToSql (state);
620 else
621 return Left_.ToSql (state) + ", " + Right_.ToSql (state);
622 }
623
624 template<typename OL, typename OR>
625 auto operator, (const AssignList<OL, OR>& tail) noexcept
626 {
627 return AssignList<AssignList<L, R>, AssignList<OL, OR>> { *this, tail };
628 }
629 };
630
631 template<ExprType Type, typename L, typename R>
633 {
634 L Left_;
635 R Right_;
636 public:
637 ExprTree (const L& l, const R& r) noexcept
638 : Left_ (l)
639 , Right_ (r)
640 {
641 }
642
643 template<typename T>
644 QString ToSql (ToSqlState<T>& state) const noexcept
645 {
647 "Incompatible types passed to a relational operator.");
648
649 return Left_.ToSql (state) + " " + TypeToSql (Type) + " " + Right_.ToSql (state);
650 }
651
652 template<typename T>
653 QSet<QString> AdditionalTables () const noexcept
654 {
655 return Left_.template AdditionalTables<T> () + Right_.template AdditionalTables<T> ();
656 }
657
658 template<typename T>
659 constexpr static bool HasAdditionalTables () noexcept
660 {
661 return L::template HasAdditionalTables<T> () || R::template HasAdditionalTables<T> ();
662 }
663 };
664
665 template<typename T>
666 class ExprTree<ExprType::LeafData, T, void>
667 {
668 T Data_;
669 public:
670 template<typename>
671 using ValueType_t = T;
672
673 ExprTree (const T& t) noexcept
674 : Data_ (t)
675 {
676 }
677
678 template<typename ObjT>
679 QString ToSql (ToSqlState<ObjT>& state) const noexcept
680 {
681 const auto& name = ":bound_" + QString::number (++state.LastID_);
682 state.BoundMembers_ [name] = ToVariantF (Data_);
683 return name;
684 }
685
686 template<typename>
687 QSet<QString> AdditionalTables () const noexcept
688 {
689 return {};
690 }
691
692 template<typename>
693 constexpr static bool HasAdditionalTables () noexcept
694 {
695 return false;
696 }
697 };
698
699 template<typename T>
700 constexpr auto AsLeafData (const T& node) noexcept
701 {
702 if constexpr (IsExprTree<T> {})
703 return node;
704 else
705 return ExprTree<ExprType::LeafData, T> { node };
706 }
707
708 template<auto... Ptr>
709 struct MemberPtrs {};
710
711 template<auto Ptr>
713 {
714 using ExpectedType_t = MemberPtrType_t<Ptr>;
715 public:
716 template<typename>
717 using ValueType_t = ExpectedType_t;
718
719 template<typename T>
720 QString ToSql (ToSqlState<T>&) const noexcept
721 {
723 }
724
725 QString GetFieldName () const noexcept
726 {
727 return detail::GetFieldNamePtr<Ptr> ();
728 }
729
730 template<typename T>
731 QSet<QString> AdditionalTables () const noexcept
732 {
733 using Seq = MemberPtrStruct_t<Ptr>;
734 if constexpr (std::is_same_v<Seq, T>)
735 return {};
736 else
737 return { Seq::ClassName () };
738 }
739
740 template<typename T>
741 constexpr static bool HasAdditionalTables () noexcept
742 {
743 return !std::is_same_v<MemberPtrStruct_t<Ptr>, T>;
744 }
745
746 auto operator= (const ExpectedType_t& r) const noexcept
747 {
748 return AssignList { *this, AsLeafData (r) };
749 }
750 };
751
752 template<>
753 class ExprTree<ExprType::ConstTrue, void, void> {};
754
756
757 template<ExprType Type, typename L, typename R>
758 auto MakeExprTree (const L& left, const R& right) noexcept
759 {
760 using EL = decltype (AsLeafData (left));
761 using ER = decltype (AsLeafData (right));
762 return ExprTree<Type, EL, ER> { AsLeafData (left), AsLeafData (right) };
763 }
764
765 template<typename L, typename R>
766 constexpr bool EitherIsExprTree () noexcept
767 {
768 if (IsExprTree<L> {})
769 return true;
770 if (IsExprTree<R> {})
771 return true;
772 return false;
773 }
774
775 template<typename L, typename R>
776 using EnableRelOp_t = std::enable_if_t<EitherIsExprTree<L, R> ()>;
777
778 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
779 auto operator< (const L& left, const R& right) noexcept
780 {
781 return MakeExprTree<ExprType::Less> (left, right);
782 }
783
784 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
785 auto operator> (const L& left, const R& right) noexcept
786 {
787 return MakeExprTree<ExprType::Greater> (left, right);
788 }
789
790 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
791 auto operator== (const L& left, const R& right) noexcept
792 {
793 return MakeExprTree<ExprType::Equal> (left, right);
794 }
795
796 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
797 auto operator!= (const L& left, const R& right) noexcept
798 {
799 return MakeExprTree<ExprType::Neq> (left, right);
800 }
801
802 template<ExprType Op>
803 struct InfixBinary {};
804 }
805
806 namespace infix
807 {
809 }
810
811 namespace detail
812 {
813 template<typename L, ExprType Op>
815 {
816 const L& Left_;
817 };
818
819 template<typename L, ExprType Op>
820 auto operator| (const L& left, InfixBinary<Op>) noexcept
821 {
822 return InfixBinaryProxy<L, Op> { left };
823 }
824
825 template<typename L, ExprType Op, typename R>
826 auto operator| (const InfixBinaryProxy<L, Op>& left, const R& right) noexcept
827 {
828 return MakeExprTree<Op> (left.Left_, right);
829 }
830
831 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
832 auto operator&& (const L& left, const R& right) noexcept
833 {
834 return MakeExprTree<ExprType::And> (left, right);
835 }
836
837 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
838 auto operator|| (const L& left, const R& right) noexcept
839 {
840 return MakeExprTree<ExprType::Or> (left, right);
841 }
842
843 template<typename F>
845 {
846 QString Sql_;
849
850 ExprTreeHandler (const QString& sql, F&& binder, int lastId) noexcept
851 : Sql_ { sql }
852 , Binder_ { std::move (binder) }
853 , LastID_ { lastId }
854 {
855 }
856 };
857
858 template<typename>
859 auto HandleExprTree (const ExprTree<ExprType::ConstTrue>&, int lastId = 0) noexcept
860 {
861 return ExprTreeHandler { "1 = 1", [] (auto&&) {}, lastId };
862 }
863
864 template<typename Seq, typename Tree,
865 typename = decltype (std::declval<Tree> ().ToSql (std::declval<ToSqlState<Seq>&> ()))>
866 auto HandleExprTree (const Tree& tree, int lastId = 0) noexcept
867 {
868 ToSqlState<Seq> state { lastId, {} };
869
870 const auto& sql = tree.ToSql (state);
871
872 return ExprTreeHandler
873 {
874 sql,
875 [state] (QSqlQuery& query)
876 {
877 for (const auto& pair : Stlize (state.BoundMembers_))
878 query.bindValue (pair.first, pair.second);
879 },
880 state.LastID_
881 };
882 }
883
885 {
886 Count,
887 Min,
888 Max
889 };
890
891 template<AggregateFunction, auto Ptr>
892 struct AggregateType {};
893
894 struct CountAll {};
895
896 inline constexpr CountAll *CountAllPtr = nullptr;
897
898 template<typename... MemberDirectionList>
899 struct OrderBy {};
900
901 template<auto... Ptrs>
902 struct GroupBy {};
903
904 struct SelectWhole {};
905
906 template<typename L, typename R>
907 struct SelectorUnion {};
908
909 template<typename T>
910 struct IsSelector : std::false_type {};
911
912 template<>
913 struct IsSelector<SelectWhole> : std::true_type {};
914
915 template<AggregateFunction Fun, auto Ptr>
916 struct IsSelector<AggregateType<Fun, Ptr>> : std::true_type {};
917
918 template<auto... Ptrs>
919 struct IsSelector<MemberPtrs<Ptrs...>> : std::true_type {};
920
921 template<typename L, typename R>
922 struct IsSelector<SelectorUnion<L, R>> : std::true_type {};
923
924 template<typename L, typename R, typename = std::enable_if_t<IsSelector<L> {} && IsSelector<R> {}>>
926 {
927 return {};
928 }
929 }
930
931 namespace sph
932 {
933 template<auto Ptr>
935
936 template<auto... Ptrs>
937 constexpr detail::MemberPtrs<Ptrs...> fields {};
938
940
941 template<auto... Ptrs>
942 struct asc {};
943
944 template<auto... Ptrs>
945 struct desc {};
946
947 template<auto Ptr = detail::CountAllPtr>
949
950 template<auto Ptr>
952
953 template<auto Ptr>
955 };
956
957 template<typename... Orders>
958 constexpr detail::OrderBy<Orders...> OrderBy {};
959
960 template<auto... Ptrs>
961 constexpr detail::GroupBy<Ptrs...> GroupBy {};
962
963 struct Limit
964 {
965 uint64_t Count;
966
967 Limit (uint64_t count) noexcept
968 : Count { count }
969 {
970 }
971 };
972
973 struct Offset
974 {
975 uint64_t Count;
976
977 Offset (uint64_t count) noexcept
978 : Count { count }
979 {
980 }
981 };
982
983 namespace detail
984 {
985 template<auto... Ptrs, size_t... Idxs>
986 auto MakeIndexedQueryHandler (MemberPtrs<Ptrs...>, std::index_sequence<Idxs...>) noexcept
987 {
988 return [] (const QSqlQuery& q, int startIdx = 0) noexcept
989 {
990 if constexpr (sizeof... (Ptrs) == 1)
991 return FromVariant<UnwrapIndirect_t<Head_t<Typelist<MemberPtrType_t<Ptrs>...>>>> {} (q.value (startIdx));
992 else
993 return std::tuple { FromVariant<UnwrapIndirect_t<MemberPtrType_t<Ptrs>>> {} (q.value (startIdx + Idxs))... };
994 };
995 }
996
997 template<auto Ptr>
998 auto MakeIndexedQueryHandler () noexcept
999 {
1000 return [] (const QSqlQuery& q, int startIdx = 0) noexcept
1001 {
1002 return FromVariant<UnwrapIndirect_t<MemberPtrType_t<Ptr>>> {} (q.value (startIdx));
1003 };
1004 }
1005
1006 template<auto... Ptrs>
1007 QStringList BuildFieldNames () noexcept
1008 {
1009 return { BuildCachedFieldsData<MemberPtrStruct_t<Ptrs>> ().QualifiedFields_.value (FieldIndex<Ptrs> ())... };
1010 }
1011
1012 enum class SelectBehaviour { Some, One };
1013
1014 struct OrderNone {};
1015 struct GroupNone {};
1016 struct LimitNone {};
1017 struct OffsetNone {};
1018
1019 template<size_t RepIdx, size_t TupIdx, typename Tuple, typename NewType>
1020 constexpr decltype (auto) GetReplaceTupleElem (Tuple&& tuple, NewType&& arg) noexcept
1021 {
1022 if constexpr (RepIdx == TupIdx)
1023 return std::forward<NewType> (arg);
1024 else
1025 return std::get<TupIdx> (tuple);
1026 }
1027
1028 template<size_t RepIdx, typename NewType, typename Tuple, size_t... TupIdxs>
1029 constexpr auto ReplaceTupleElemImpl (Tuple&& tuple, NewType&& arg, std::index_sequence<TupIdxs...>) noexcept
1030 {
1031 return std::tuple
1032 {
1033 GetReplaceTupleElem<RepIdx, TupIdxs> (std::forward<Tuple> (tuple), std::forward<NewType> (arg))...
1034 };
1035 }
1036
1037 template<size_t RepIdx, typename NewType, typename... TupleArgs>
1038 constexpr auto ReplaceTupleElem (std::tuple<TupleArgs...>&& tuple, NewType&& arg) noexcept
1039 {
1040 return ReplaceTupleElemImpl<RepIdx> (std::move (tuple),
1041 std::forward<NewType> (arg),
1042 std::index_sequence_for<TupleArgs...> {});
1043 }
1044
1045 template<typename Seq, typename T>
1047 {
1048 constexpr static int Value = 1;
1049 };
1050
1051 template<typename Seq, typename... Args>
1052 struct DetectShift<Seq, std::tuple<Args...>>
1053 {
1054 constexpr static int Value = (DetectShift<Seq, Args>::Value + ...);
1055 };
1056
1057 template<typename Seq>
1058 struct DetectShift<Seq, Seq>
1059 {
1060 constexpr static int Value = SeqSize<Seq>;
1061 };
1062
1063 template<typename... LArgs, typename... RArgs>
1064 auto Combine (std::tuple<LArgs...>&& left, std::tuple<RArgs...>&& right) noexcept
1065 {
1066 return std::tuple_cat (std::move (left), std::move (right));
1067 }
1068
1069 template<typename... LArgs, typename R>
1070 auto Combine (std::tuple<LArgs...>&& left, const R& right) noexcept
1071 {
1072 return std::tuple_cat (std::move (left), std::tuple { right });
1073 }
1074
1075 template<typename L, typename... RArgs>
1076 auto Combine (const L& left, std::tuple<RArgs...>&& right) noexcept
1077 {
1078 return std::tuple_cat (std::tuple { left }, std::move (right));
1079 }
1080
1081 template<typename L, typename R>
1082 auto Combine (const L& left, const R& right) noexcept
1083 {
1084 return std::tuple { left, right };
1085 }
1086
1088 {
1089 struct All {};
1090 struct First {};
1091 };
1092
1093 template<typename L, typename R>
1094 constexpr auto CombineBehaviour (L, R) noexcept
1095 {
1096 if constexpr (std::is_same_v<L, ResultBehaviour::First> && std::is_same_v<R, ResultBehaviour::First>)
1097 return ResultBehaviour::First {};
1098 else
1099 return ResultBehaviour::All {};
1100 }
1101
1102 template<typename ResList>
1103 decltype (auto) HandleResultBehaviour (ResultBehaviour::All, ResList&& list) noexcept
1104 {
1105 return std::forward<ResList> (list);
1106 }
1107
1108 template<typename ResList>
1109 auto HandleResultBehaviour (ResultBehaviour::First, ResList&& list) noexcept
1110 {
1111 return list.value (0);
1112 }
1113
1114 template<typename F, typename R>
1116 {
1117 QString Fields_;
1120 };
1121
1122 template<typename F, typename R>
1124
1126 {
1127 protected:
1128 const QSqlDatabase DB_;
1129 const QString LimitNone_;
1130
1131 SelectWrapperCommon (const QSqlDatabase& db, const QString& limitNone)
1132 : DB_ { db }
1133 , LimitNone_ { limitNone }
1134 {
1135 }
1136
1137 auto RunQuery (const QString& fields, const QString& from,
1138 QString where, std::function<void (QSqlQuery&)>&& binder,
1139 const QString& orderStr,
1140 const QString& groupStr,
1141 const QString& limitOffsetStr) const
1142 {
1143 if (!where.isEmpty ())
1144 where.prepend (" WHERE ");
1145
1146 const auto& queryStr = "SELECT " + fields +
1147 " FROM " + from +
1148 where +
1149 orderStr +
1150 groupStr +
1151 limitOffsetStr;
1152
1153 QSqlQuery query { DB_ };
1154 query.prepare (queryStr);
1155 if (binder)
1156 binder (query);
1157
1158 if (!query.exec ())
1159 {
1160 DBLock::DumpError (query);
1161 throw QueryException ("fetch query execution failed", std::make_shared<QSqlQuery> (query));
1162 }
1163
1164 return query;
1165 }
1166
1167 QString HandleLimitOffset (LimitNone, OffsetNone) const noexcept
1168 {
1169 return {};
1170 }
1171
1172 QString HandleLimitOffset (Limit limit, OffsetNone) const noexcept
1173 {
1174 return " LIMIT " + QString::number (limit.Count);
1175 }
1176
1177 template<typename L>
1178 QString HandleLimitOffset (L limit, Offset offset) const noexcept
1179 {
1180 QString limitStr;
1181 if constexpr (std::is_same_v<std::decay_t<L>, LimitNone>)
1182 {
1183 Q_UNUSED (limit)
1184 limitStr = LimitNone_;
1185 }
1186 else if constexpr (std::is_integral_v<L>)
1187 limitStr = QString::number (limit);
1188 else
1189 limitStr = QString::number (limit.Count);
1190 return " LIMIT " + limitStr +
1191 " OFFSET " + QString::number (offset.Count);
1192 }
1193 };
1194
1195 template<typename T, SelectBehaviour SelectBehaviour>
1197 {
1198 const CachedFieldsData Cached_;
1199
1200 template<typename ParamsTuple>
1201 struct Builder
1202 {
1203 const SelectWrapper& W_;
1204 ParamsTuple Params_;
1205
1206 template<typename NewTuple>
1207 constexpr auto RepTuple (NewTuple&& tuple) noexcept
1208 {
1209 return Builder<NewTuple> { W_, tuple };
1210 }
1211
1212 template<typename U>
1213 constexpr auto Select (U&& selector) && noexcept
1214 {
1215 return RepTuple (ReplaceTupleElem<0> (std::move (Params_), std::forward<U> (selector)));
1216 }
1217
1218 template<typename U>
1219 constexpr auto Where (U&& tree) && noexcept
1220 {
1221 return RepTuple (ReplaceTupleElem<1> (std::move (Params_), std::forward<U> (tree)));
1222 }
1223
1224 template<typename U>
1225 constexpr auto Order (U&& order) && noexcept
1226 {
1227 return RepTuple (ReplaceTupleElem<2> (std::move (Params_), std::forward<U> (order)));
1228 }
1229
1230 template<typename U>
1231 constexpr auto Group (U&& group) && noexcept
1232 {
1233 return RepTuple (ReplaceTupleElem<3> (std::move (Params_), std::forward<U> (group)));
1234 }
1235
1236 template<typename U = Limit>
1237 constexpr auto Limit (U&& limit) && noexcept
1238 {
1239 return RepTuple (ReplaceTupleElem<4> (std::move (Params_), std::forward<U> (limit)));
1240 }
1241
1242 template<typename U = Offset>
1243 constexpr auto Offset (U&& offset) && noexcept
1244 {
1245 return RepTuple (ReplaceTupleElem<5> (std::move (Params_), std::forward<U> (offset)));
1246 }
1247
1248 auto operator() () &&
1249 {
1250 return std::apply (W_, Params_);
1251 }
1252
1253 template<auto... Ptrs>
1254 constexpr auto Group () && noexcept
1255 {
1256 return std::move (*this).Group (GroupBy<Ptrs...> {});
1257 }
1258 };
1259 public:
1260 template<typename ImplFactory>
1261 SelectWrapper (const QSqlDatabase& db, const CachedFieldsData& data, ImplFactory&& factory) noexcept
1262 : SelectWrapperCommon { db, factory.LimitNone }
1263 , Cached_ { data }
1264 {
1265 }
1266
1267 auto Build () const noexcept
1268 {
1269 std::tuple defParams
1270 {
1271 SelectWhole {},
1273 OrderNone {},
1274 GroupNone {},
1275 LimitNone {},
1276 OffsetNone {}
1277 };
1278 return Builder<decltype (defParams)> { *this, defParams };
1279 }
1280
1281 auto operator() () const
1282 {
1283 return (*this) (SelectWhole {}, ConstTrueTree_v);
1284 }
1285
1286 template<typename Single>
1287 auto operator() (Single&& single) const
1288 {
1289 if constexpr (IsExprTree<std::decay_t<Single>> {})
1290 return (*this) (SelectWhole {}, std::forward<Single> (single));
1291 else
1292 return (*this) (std::forward<Single> (single), ConstTrueTree_v);
1293 }
1294
1295 template<
1296 typename Selector,
1297 ExprType Type, typename L, typename R,
1298 typename Order = OrderNone,
1299 typename Group = GroupNone,
1300 typename Limit = LimitNone,
1301 typename Offset = OffsetNone
1302 >
1303 auto operator() (Selector selector,
1304 const ExprTree<Type, L, R>& tree,
1305 Order order = OrderNone {},
1306 Group group = GroupNone {},
1307 Limit limit = LimitNone {},
1308 Offset offset = OffsetNone {}) const
1309 {
1310 const auto& [where, binder, _] = HandleExprTree<T> (tree);
1311 Q_UNUSED (_);
1312 const auto& [fields, initializer, resultBehaviour] = HandleSelector (std::forward<Selector> (selector));
1313 return HandleResultBehaviour (resultBehaviour,
1314 Select (fields, BuildFromClause (tree),
1315 where, binder,
1316 initializer,
1317 HandleOrder (std::forward<Order> (order)),
1318 HandleGroup (std::forward<Group> (group)),
1319 HandleLimitOffset (std::forward<Limit> (limit), std::forward<Offset> (offset))));
1320 }
1321 private:
1322 template<typename Binder, typename Initializer>
1323 auto Select (const QString& fields, const QString& from,
1324 const QString& where, Binder&& binder,
1325 Initializer&& initializer,
1326 const QString& orderStr,
1327 const QString& groupStr,
1328 const QString& limitOffsetStr) const
1329 {
1330 std::function<void (QSqlQuery&)> binderFunc;
1331 if constexpr (!std::is_same_v<Void, std::decay_t<Binder>>)
1332 binderFunc = binder;
1333 auto query = RunQuery (fields, from, where, std::move (binderFunc), orderStr, groupStr, limitOffsetStr);
1334
1335 if constexpr (SelectBehaviour == SelectBehaviour::Some)
1336 {
1337 QList<std::result_of_t<Initializer (QSqlQuery)>> result;
1338 while (query.next ())
1339 result << initializer (query);
1340 return result;
1341 }
1342 else
1343 {
1344 using RetType_t = std::optional<std::result_of_t<Initializer (QSqlQuery)>>;
1345 return query.next () ?
1346 RetType_t { initializer (query) } :
1347 RetType_t {};
1348 }
1349 }
1350
1351 template<ExprType Type, typename L, typename R>
1352 QString BuildFromClause (const ExprTree<Type, L, R>& tree) const noexcept
1353 {
1354 if constexpr (Type != ExprType::ConstTrue)
1355 {
1356 auto result = Cached_.Table_;
1357 for (const auto& item : tree.template AdditionalTables<T> ())
1358 result += ", " + item;
1359 return result;
1360 }
1361 else
1362 return Cached_.Table_;
1363 }
1364
1365 auto HandleSelector (SelectWhole) const noexcept
1366 {
1368 {
1369 Cached_.QualifiedFields_.join (", "),
1370 [] (const QSqlQuery& q, int startIdx = 0)
1371 {
1372 return InitializeFromQuery<T> (q, SeqIndices<T>, startIdx);
1373 },
1374 ResultBehaviour::All {}
1375 };
1376 }
1377
1378 template<auto... Ptrs>
1379 auto HandleSelector (MemberPtrs<Ptrs...> ptrs) const noexcept
1380 {
1382 {
1383 BuildFieldNames<Ptrs...> ().join (", "),
1384 MakeIndexedQueryHandler (ptrs, std::make_index_sequence<sizeof... (Ptrs)> {}),
1385 ResultBehaviour::All {}
1386 };
1387 }
1388
1389 auto HandleSelector (AggregateType<AggregateFunction::Count, CountAllPtr>) const noexcept
1390 {
1392 {
1393 "count(1)",
1394 [] (const QSqlQuery& q, int startIdx = 0) { return q.value (startIdx).toLongLong (); },
1395 ResultBehaviour::First {}
1396 };
1397 }
1398
1399 template<auto Ptr>
1400 auto HandleSelector (AggregateType<AggregateFunction::Count, Ptr>) const noexcept
1401 {
1403 {
1404 "count(" + GetQualifiedFieldNamePtr<Ptr> () + ")",
1405 [] (const QSqlQuery& q, int startIdx = 0) { return q.value (startIdx).toLongLong (); },
1406 ResultBehaviour::First {}
1407 };
1408 }
1409
1410 template<auto Ptr>
1411 auto HandleSelector (AggregateType<AggregateFunction::Min, Ptr>) const noexcept
1412 {
1414 {
1415 "min(" + GetQualifiedFieldNamePtr<Ptr> () + ")",
1416 MakeIndexedQueryHandler<Ptr> (),
1417 ResultBehaviour::First {}
1418 };
1419 }
1420
1421 template<auto Ptr>
1422 auto HandleSelector (AggregateType<AggregateFunction::Max, Ptr>) const noexcept
1423 {
1425 {
1426 "max(" + GetQualifiedFieldNamePtr<Ptr> () + ")",
1427 MakeIndexedQueryHandler<Ptr> (),
1428 ResultBehaviour::First {}
1429 };
1430 }
1431
1432 template<typename L, typename R>
1433 auto HandleSelector (SelectorUnion<L, R>) const noexcept
1434 {
1435 const auto& lSel = HandleSelector (L {});
1436 const auto& rSel = HandleSelector (R {});
1437
1438 const auto& lHandler = lSel.Initializer_;
1439 const auto& rHandler = rSel.Initializer_;
1440
1442 {
1443 lSel.Fields_ + ", " + rSel.Fields_,
1444 [lHandler, rHandler] (const QSqlQuery& q, int startIdx = 0)
1445 {
1446 constexpr auto shift = DetectShift<T, decltype (lHandler (q))>::Value;
1447 return Combine (lHandler (q, startIdx), rHandler (q, startIdx + shift));
1448 },
1449 CombineBehaviour (lSel.Behaviour_, rSel.Behaviour_)
1450 };
1451 }
1452
1453 QString HandleOrder (OrderNone) const noexcept
1454 {
1455 return {};
1456 }
1457
1458 template<auto... Ptrs>
1459 QList<QString> HandleSuborder (sph::asc<Ptrs...>) const noexcept
1460 {
1461 return { (GetQualifiedFieldNamePtr<Ptrs> () + " ASC")... };
1462 }
1463
1464 template<auto... Ptrs>
1465 QList<QString> HandleSuborder (sph::desc<Ptrs...>) const noexcept
1466 {
1467 return { (GetQualifiedFieldNamePtr<Ptrs> () + " DESC")... };
1468 }
1469
1470 template<typename... Suborders>
1471 QString HandleOrder (OrderBy<Suborders...>) const noexcept
1472 {
1473 return " ORDER BY " + QStringList { Concat (QList { HandleSuborder (Suborders {})... }) }.join (", ");
1474 }
1475
1476 QString HandleGroup (GroupNone) const noexcept
1477 {
1478 return {};
1479 }
1480
1481 template<auto... Ptrs>
1482 QString HandleGroup (GroupBy<Ptrs...>) const noexcept
1483 {
1484 return " GROUP BY " + QStringList { GetQualifiedFieldNamePtr<Ptrs> ()... }.join (", ");
1485 }
1486 };
1487
1488 template<typename T>
1490 {
1491 const QSqlDatabase DB_;
1492 const QString Table_;
1493 public:
1494 DeleteByFieldsWrapper (const QSqlDatabase& db, const CachedFieldsData& data) noexcept
1495 : DB_ { db }
1496 , Table_ (data.Table_)
1497 {
1498 }
1499
1500 template<ExprType Type, typename L, typename R>
1501 void operator() (const ExprTree<Type, L, R>& tree) const noexcept
1502 {
1503 const auto& [where, binder, _] = HandleExprTree<T> (tree);
1504 Q_UNUSED (_);
1505
1506 const auto& selectAll = "DELETE FROM " + Table_ +
1507 " WHERE " + where;
1508
1509 QSqlQuery query { DB_ };
1510 query.prepare (selectAll);
1511 binder (query);
1512 query.exec ();
1513 }
1514 };
1515
1516 template<typename T, bool HasPKey = HasPKey<T>>
1518 {
1519 const QSqlDatabase DB_;
1520 const QString Table_;
1521
1522 std::function<void (T)> Updater_;
1523 public:
1524 AdaptUpdate (const QSqlDatabase& db, const CachedFieldsData& data) noexcept
1525 : DB_ { db }
1526 , Table_ { data.Table_ }
1527 {
1528 if constexpr (HasPKey)
1529 {
1530 constexpr auto index = FindPKey<T>::result_type::value;
1531
1532 auto statements = Util::ZipWith<QList> (data.Fields_, data.BoundFields_,
1533 [] (const QString& s1, const QString& s2) { return s1 + " = " + s2; });
1534 auto wherePart = statements.takeAt (index);
1535 const auto& update = "UPDATE " + data.Table_ +
1536 " SET " + statements.join (", ") +
1537 " WHERE " + wherePart;
1538
1539 const auto updateQuery = std::make_shared<QSqlQuery> (db);
1540 updateQuery->prepare (update);
1541 Updater_ = MakeInserter<T> (data, updateQuery, true);
1542 }
1543 }
1544
1545 template<bool B = HasPKey>
1546 std::enable_if_t<B> operator() (const T& seq)
1547 {
1548 Updater_ (seq);
1549 }
1550
1551 template<typename SL, typename SR, ExprType WType, typename WL, typename WR>
1553 {
1554 static_assert (!ExprTree<WType, WL, WR>::template HasAdditionalTables<T> (),
1555 "joins in update statements are not supported by SQL");
1556
1557 const auto& [setClause, setBinder, setLast] = HandleExprTree<T> (set);
1558 const auto& [whereClause, whereBinder, _] = HandleExprTree<T> (where, setLast);
1559
1560 const auto& update = "UPDATE " + Table_ +
1561 " SET " + setClause +
1562 " WHERE " + whereClause;
1563
1564 QSqlQuery query { DB_ };
1565 query.prepare (update);
1566 setBinder (query);
1567 whereBinder (query);
1568 if (!query.exec ())
1569 {
1570 DBLock::DumpError (query);
1571 throw QueryException ("update query execution failed", std::make_shared<QSqlQuery> (query));
1572 }
1573
1574 return query.numRowsAffected ();
1575 }
1576 };
1577
1578 template<typename T>
1579 using ConstraintsDetector = typename T::Constraints;
1580
1581 template<typename T>
1583
1584 template<typename T>
1586
1587 template<int... Fields>
1589 {
1590 QString operator() (const CachedFieldsData& data) const noexcept
1591 {
1592 return "UNIQUE (" + QStringList { data.Fields_.value (Fields)... }.join (", ") + ")";
1593 }
1594 };
1595
1596 template<int... Fields>
1598 {
1599 QString operator() (const CachedFieldsData& data) const noexcept
1600 {
1601 return "PRIMARY KEY (" + QStringList { data.Fields_.value (Fields)... }.join (", ") + ")";
1602 }
1603 };
1604
1605 template<typename... Args>
1607 {
1608 return { ExtractConstraintFields<Args> {} (data)... };
1609 }
1610
1611 template<typename ImplFactory, typename T, size_t... Indices>
1612 QList<QString> GetTypes (std::index_sequence<Indices...>) noexcept
1613 {
1615 }
1616
1617 template<typename ImplFactory, typename T>
1618 QString AdaptCreateTable (const CachedFieldsData& data) noexcept
1619 {
1620 const auto& types = GetTypes<ImplFactory, T> (SeqIndices<T>);
1621
1622 const auto& constraints = GetConstraintsStringList (ConstraintsType<T> {}, data);
1623 const auto& constraintsStr = constraints.isEmpty () ?
1624 QString {} :
1625 (", " + constraints.join (", "));
1626
1627 const auto& statements = Util::ZipWith<QList> (types, data.Fields_,
1628 [] (const QString& type, const QString& field) { return field + " " + type; });
1629 return "CREATE TABLE " +
1630 data.Table_ +
1631 " (" +
1632 statements.join (", ") +
1633 constraintsStr +
1634 ");";
1635 }
1636 }
1637
1638 template<auto... Ptrs>
1640 {
1641 return { { detail::BuildCachedFieldsData<MemberPtrStruct_t<Ptrs>> ().Fields_.value (detail::FieldIndex<Ptrs> ())... } };
1642 }
1643
1644 template<typename Seq>
1646 {
1647 static_assert (detail::HasPKey<Seq>, "Sequence does not have any primary keys");
1648 return { { detail::GetFieldName<Seq, detail::FindPKey<Seq>::result_type::value> () } };
1649 }
1650
1651 template<typename T>
1653 {
1657
1661
1662 using ObjectType_t = T;
1663 };
1664
1665 template<typename T, typename ImplFactory = detail::SQLite::ImplFactory>
1666 ObjectInfo<T> Adapt (const QSqlDatabase& db)
1667 {
1668 const auto& cachedData = detail::BuildCachedFieldsData<T> ();
1669
1670 if (!db.tables ().contains (cachedData.Table_, Qt::CaseInsensitive))
1671 RunTextQuery (db, detail::AdaptCreateTable<ImplFactory, T> (cachedData));
1672
1673 ImplFactory factory;
1674
1675 return
1676 {
1677 { db, cachedData, factory },
1678 { db, cachedData },
1679 { db, cachedData },
1680
1681 { db, cachedData, factory },
1682 { db, cachedData, factory },
1683 { db, cachedData }
1684 };
1685 }
1686
1687 template<typename T>
1688 using ObjectInfo_ptr = std::shared_ptr<ObjectInfo<T>>;
1689
1690 template<typename T, typename ImplFactory = SQLiteImplFactory>
1691 ObjectInfo_ptr<T> AdaptPtr (const QSqlDatabase& db)
1692 {
1693 return std::make_shared<ObjectInfo<T>> (Adapt<T, ImplFactory> (db));
1694 }
1695
1696 namespace detail
1697 {
1698 template<size_t Idx, typename Tuple>
1699 using UnderlyingObject_t = typename std::decay_t<std::tuple_element_t<Idx, Tuple>>::element_type::ObjectType_t;
1700
1701 template<typename ImplFactory, typename Tuple, size_t... Idxs>
1702 void AdaptPtrs (const QSqlDatabase& db, Tuple& tuple, std::index_sequence<Idxs...>)
1703 {
1704 ((std::get<Idxs> (tuple) = AdaptPtr<UnderlyingObject_t<Idxs, Tuple>, ImplFactory> (db)), ...);
1705 }
1706 }
1707
1708 template<typename ImplFactory, typename Tuple>
1709 void AdaptPtrs (const QSqlDatabase& db, Tuple& tuple)
1710 {
1711 detail::AdaptPtrs<ImplFactory> (db, tuple, std::make_index_sequence<std::tuple_size_v<Tuple>> {});
1712 }
1713}
1714}
1715}
static UTIL_DB_API void DumpError(const QSqlError &error)
Dumps the error to the qWarning() stream.
Definition: dblock.cpp:68
A somewhat "strong" typedef.
Definition: newtype.h:33
QueryException(const std::string &str, const QSqlQuery_ptr &q)
Definition: oral.h:58
const QSqlQuery & GetQuery() const
Definition: oral.h:71
~QueryException() noexcept=default
const QSqlQuery_ptr & GetQueryPtr() const
Definition: oral.h:66
AdaptInsert(const QSqlDatabase &db, CachedFieldsData data, ImplFactory &&factory) noexcept
Definition: oral.h:389
auto operator()(Seq &t, InsertAction action=InsertAction::Default) const
Definition: oral.h:395
AdaptUpdate(const QSqlDatabase &db, const CachedFieldsData &data) noexcept
Definition: oral.h:1524
std::enable_if_t< B > operator()(const T &seq)
Definition: oral.h:1546
AssignList(const L &l, const R &r) noexcept
Definition: oral.h:609
auto operator,(const AssignList< OL, OR > &tail) noexcept
Definition: oral.h:625
QString ToSql(ToSqlState< T > &state) const noexcept
Definition: oral.h:616
DeleteByFieldsWrapper(const QSqlDatabase &db, const CachedFieldsData &data) noexcept
Definition: oral.h:1494
void operator()(const ExprTree< Type, L, R > &tree) const noexcept
Definition: oral.h:1501
QString ToSql(ToSqlState< ObjT > &state) const noexcept
Definition: oral.h:679
QSet< QString > AdditionalTables() const noexcept
Definition: oral.h:687
static constexpr bool HasAdditionalTables() noexcept
Definition: oral.h:693
static constexpr bool HasAdditionalTables() noexcept
Definition: oral.h:659
QString ToSql(ToSqlState< T > &state) const noexcept
Definition: oral.h:644
QSet< QString > AdditionalTables() const noexcept
Definition: oral.h:653
ExprTree(const L &l, const R &r) noexcept
Definition: oral.h:637
QString HandleLimitOffset(Limit limit, OffsetNone) const noexcept
Definition: oral.h:1172
QString HandleLimitOffset(L limit, Offset offset) const noexcept
Definition: oral.h:1178
QString HandleLimitOffset(LimitNone, OffsetNone) const noexcept
Definition: oral.h:1167
auto RunQuery(const QString &fields, const QString &from, QString where, std::function< void(QSqlQuery &)> &&binder, const QString &orderStr, const QString &groupStr, const QString &limitOffsetStr) const
Definition: oral.h:1137
SelectWrapperCommon(const QSqlDatabase &db, const QString &limitNone)
Definition: oral.h:1131
SelectWrapper(const QSqlDatabase &db, const CachedFieldsData &data, ImplFactory &&factory) noexcept
Definition: oral.h:1261
auto Build() const noexcept
Definition: oral.h:1267
Fields_t Fields_
QSqlQuery RunTextQuery(const QSqlDatabase &db, const QString &text)
Runs the given query text on the given db.
Definition: util.cpp:17
Type
Describes the various types of XDG .desktop files.
Definition: itemtypes.h:24
auto operator>(const L &left, const R &right) noexcept
Definition: oral.h:785
QString GetQualifiedFieldNamePtr() noexcept
Definition: oral.h:169
typename boost::fusion::result_of::value_at< Seq, Idx >::type ValueAt_t
Definition: oral.h:325
constexpr auto AsLeafData(const T &node) noexcept
Definition: oral.h:700
auto operator||(const L &left, const R &right) noexcept
Definition: oral.h:838
T InitializeFromQuery(const QSqlQuery &q, std::index_sequence< Indices... >, int startIdx) noexcept
Definition: oral.h:478
auto Combine(std::tuple< LArgs... > &&left, std::tuple< RArgs... > &&right) noexcept
Definition: oral.h:1064
constexpr bool BaseTypeCustomized
Definition: oral.h:185
decltype(std::declval< UnwrapIndirect_t< typename L::template ValueType_t< Seq > > >()==std::declval< UnwrapIndirect_t< typename R::template ValueType_t< Seq > > >()) ComparableDetector
Definition: oral.h:577
QList< QString > GetTypes(std::index_sequence< Indices... >) noexcept
Definition: oral.h:1612
QString GetFieldName() noexcept
Definition: oral.h:96
typename std::conditional_t< IsIndirect< T > {}, T, WrapDirect< T > >::value_type UnwrapIndirect_t
Definition: oral.h:573
constexpr CountAll * CountAllPtr
Definition: oral.h:896
constexpr auto AreComparableTypes
Definition: oral.h:580
constexpr auto SeqSize
Definition: oral.h:102
constexpr auto ConstTrueTree_v
Definition: oral.h:755
void AdaptPtrs(const QSqlDatabase &db, Tuple &tuple, std::index_sequence< Idxs... >)
Definition: oral.h:1702
typename T::Constraints ConstraintsDetector
Definition: oral.h:1579
QVariant ToVariantF(const T &t) noexcept
Definition: oral.h:294
constexpr bool IsRelational(ExprType type) noexcept
Definition: oral.h:546
auto MakeIndexedQueryHandler() noexcept
Definition: oral.h:998
std::enable_if_t< EitherIsExprTree< L, R >()> EnableRelOp_t
Definition: oral.h:776
typename boost::fusion::result_of::value_at_c< Seq, Idx >::type ValueAtC_t
Definition: oral.h:322
boost::mpl::int_< FindPKey< Seq >::result_type::value > FindPKeyDetector
Definition: oral.h:347
std::unique_ptr< IInsertQueryBuilder > IInsertQueryBuilder_ptr
Definition: impldefs.h:27
decltype(new T { std::declval< Args >()... }) AggregateDetector_t
Definition: oral.h:475
constexpr auto HasPKey
Definition: oral.h:350
constexpr decltype(auto) GetReplaceTupleElem(Tuple &&tuple, NewType &&arg) noexcept
Definition: oral.h:1020
SelectorUnion< L, R > operator+(L, R) noexcept
Definition: oral.h:925
constexpr bool TypeNameCustomized
Definition: oral.h:179
constexpr auto ReplaceTupleElem(std::tuple< TupleArgs... > &&tuple, NewType &&arg) noexcept
Definition: oral.h:1038
decltype(auto) HandleResultBehaviour(ResultBehaviour::All, ResList &&list) noexcept
Definition: oral.h:1103
QStringList GetConstraintsStringList(Constraints< Args... >, const CachedFieldsData &data) noexcept
Definition: oral.h:1606
auto operator!=(const L &left, const R &right) noexcept
Definition: oral.h:797
constexpr bool EitherIsExprTree() noexcept
Definition: oral.h:766
HandleSelectorResult(QString, F, R) -> HandleSelectorResult< F, R >
auto MakeExprTree(const L &left, const R &right) noexcept
Definition: oral.h:758
constexpr size_t FieldIndex() noexcept
Definition: oral.h:141
constexpr auto ReplaceTupleElemImpl(Tuple &&tuple, NewType &&arg, std::index_sequence< TupIdxs... >) noexcept
Definition: oral.h:1029
auto MakeInserter(const CachedFieldsData &data, const QSqlQuery_ptr &insertQuery, bool bindPrimaryKey) noexcept
Definition: oral.h:300
typename T::BaseType BaseTypeDetector
Definition: oral.h:182
QStringList BuildFieldNames() noexcept
Definition: oral.h:1007
auto operator==(const L &left, const R &right) noexcept
Definition: oral.h:791
decltype(T::TypeName) TypeNameDetector
Definition: oral.h:176
QString GetFieldNamePtr() noexcept
Definition: oral.h:162
auto operator<(const L &left, const R &right) noexcept
Definition: oral.h:779
constexpr auto SeqIndices
Definition: oral.h:105
Util::IsDetected_t< Constraints<>, ConstraintsDetector, T > ConstraintsType
Definition: oral.h:1582
constexpr auto HasAutogenPKey() noexcept
Definition: oral.h:353
auto HandleExprTree(const ExprTree< ExprType::ConstTrue > &, int lastId=0) noexcept
Definition: oral.h:859
decltype(std::declval< U >().FieldNameMorpher(QString {})) MorpherDetector
Definition: oral.h:80
constexpr auto CombineBehaviour(L, R) noexcept
Definition: oral.h:1094
typename std::decay_t< std::tuple_element_t< Idx, Tuple > >::element_type::ObjectType_t UnderlyingObject_t
Definition: oral.h:1699
QString TypeToSql(ExprType type) noexcept
Definition: oral.h:514
QString MorphFieldName(QString str) noexcept
Definition: oral.h:83
auto operator|(const L &left, InfixBinary< Op >) noexcept
Definition: oral.h:820
CachedFieldsData BuildCachedFieldsData() noexcept
Definition: oral.h:372
auto operator&&(const L &left, const R &right) noexcept
Definition: oral.h:832
QString AdaptCreateTable(const CachedFieldsData &data) noexcept
Definition: oral.h:1618
constexpr detail::InfixBinary< detail::ExprType::Like > like
Definition: oral.h:808
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition: oral.h:934
constexpr detail::SelectWhole all
Definition: oral.h:939
constexpr detail::AggregateType< detail::AggregateFunction::Count, Ptr > count
Definition: oral.h:948
constexpr detail::AggregateType< detail::AggregateFunction::Min, Ptr > min
Definition: oral.h:951
constexpr detail::MemberPtrs< Ptrs... > fields
Definition: oral.h:937
constexpr detail::AggregateType< detail::AggregateFunction::Max, Ptr > max
Definition: oral.h:954
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
Definition: oral.h:1691
void AdaptPtrs(const QSqlDatabase &db, Tuple &tuple)
Definition: oral.h:1709
constexpr detail::OrderBy< Orders... > OrderBy
Definition: oral.h:958
std::shared_ptr< QSqlQuery > QSqlQuery_ptr
Definition: oral.h:52
constexpr detail::GroupBy< Ptrs... > GroupBy
Definition: oral.h:961
std::shared_ptr< ObjectInfo< T > > ObjectInfo_ptr
Definition: oral.h:1688
ObjectInfo< T > Adapt(const QSqlDatabase &db)
Definition: oral.h:1666
typename AsTypelist< T >::Result_t AsTypelist_t
Definition: typelist.h:140
auto Stlize(Assoc &&assoc)
Converts an Qt's associative sequence assoc to an STL-like iteratable range.
Definition: qtutil.h:43
Container< T > Concat(const Container< Container< T > > &containers)
Definition: prelude.h:165
void Unreachable()
Definition: unreachable.h:15
MemberTypeStruct_t< decltype(Ptr)> MemberPtrStruct_t
Definition: typegetter.h:64
std::tuple_element_t< 0, decltype(detail::TypeGetter(*static_cast< F * >(nullptr)))> RetType_t
Definition: typegetter.h:37
auto Map(Container &&c, F f)
Definition: prelude.h:143
typename detail::IsDetected< Type, void, Op, Args... >::type IsDetected_t
Definition: detector.h:38
MemberTypeType_t< decltype(Ptr)> MemberPtrType_t
Definition: typegetter.h:61
Definition: constants.h:15
STL namespace.
T operator()(const QVariant &var) const noexcept
Definition: oral.h:268
static struct LC::Util::oral::InsertAction::DefaultTag Default
uint64_t Count
Definition: oral.h:965
Limit(uint64_t count) noexcept
Definition: oral.h:967
detail::AdaptUpdate< T > Update
Definition: oral.h:1655
detail::SelectWrapper< T, detail::SelectBehaviour::Some > Select
Definition: oral.h:1658
detail::AdaptDelete< T > Delete
Definition: oral.h:1656
detail::AdaptInsert< T > Insert
Definition: oral.h:1654
detail::DeleteByFieldsWrapper< T > DeleteBy
Definition: oral.h:1660
detail::SelectWrapper< T, detail::SelectBehaviour::One > SelectOne
Definition: oral.h:1659
uint64_t Count
Definition: oral.h:975
Offset(uint64_t count) noexcept
Definition: oral.h:977
QVariant operator()(const T &t) const noexcept
Definition: oral.h:248
QString operator()() const noexcept
Definition: oral.h:191
AdaptDelete(const QSqlDatabase &, const CachedFieldsData &, std::enable_if_t<!B > *=nullptr) noexcept
Definition: oral.h:463
AdaptDelete(const QSqlDatabase &db, const CachedFieldsData &data, std::enable_if_t< B > *=nullptr) noexcept
Definition: oral.h:442
std::function< void(Seq)> Deleter_
Definition: oral.h:439
std::enable_if_t< B > operator()(const Seq &seq)
Definition: oral.h:468
static constexpr auto Ptr() noexcept
Definition: oral.h:128
static constexpr auto Index() noexcept
Definition: oral.h:134
static constexpr int Value
Definition: oral.h:1048
ExprTreeHandler(const QString &sql, F &&binder, int lastId) noexcept
Definition: oral.h:850
typename std::conditional_t< IsPKey< ValueAt_t< Seq, MemberIdx > >::value, Lazy< MemberIdx >, Lazy< FindPKey< Seq, typename boost::mpl::next< MemberIdx >::type > > >::type result_type
Definition: oral.h:343
QStringList operator()() const noexcept
Definition: oral.h:110