LeechCraft 0.6.70-16373-g319c272718
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/preprocessor/stringize.hpp>
16#include <boost/preprocessor/tuple.hpp>
17#include <QStringList>
18#include <QDateTime>
19#include <QPair>
20#include <QSqlQuery>
21#include <QSqlRecord>
22#include <QVariant>
23#include <QDateTime>
24#include <QtDebug>
26#include <util/sll/prelude.h>
27#include <util/sll/typelist.h>
28#include <util/sll/typegetter.h>
29#include <util/sll/detector.h>
30#include <util/db/dblock.h>
31#include <util/db/util.h>
32#include "oraltypes.h"
33#include "sqliteimpl.h"
34
35#ifndef ORAL_ADAPT_STRUCT
36
37#define ORAL_STRING_FIELD(_, index, tuple) \
38 if constexpr (Idx == index) \
39 return CtString { BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(index, tuple)) };
40
41#define ORAL_GET_FIELD(_, index, tuple) \
42 if constexpr (Idx == index) \
43 return s.BOOST_PP_TUPLE_ELEM(index, tuple);
44
45#define ORAL_GET_FIELD_INDEX_IMPL(index, sname, field) \
46 template<> \
47 constexpr size_t FieldIndexAccess<sname>::FieldIndex<&sname::field> () { return index; }
48
49#define ORAL_GET_FIELD_INDEX(_, index, args) \
50 ORAL_GET_FIELD_INDEX_IMPL(index, BOOST_PP_TUPLE_ELEM(0, args), BOOST_PP_TUPLE_ELEM(index, BOOST_PP_TUPLE_ELEM(1, args)))
51
52#define ORAL_ADAPT_STRUCT(sname, ...) \
53namespace LC::Util::oral \
54{ \
55 template<> \
56 constexpr auto SeqSize<sname> = BOOST_PP_TUPLE_SIZE((__VA_ARGS__)); \
57 \
58 template<> \
59 struct MemberNames<sname> \
60 { \
61 template<size_t Idx> \
62 constexpr static auto Get () \
63 { \
64 BOOST_PP_REPEAT(BOOST_PP_TUPLE_SIZE((__VA_ARGS__)), ORAL_STRING_FIELD, (__VA_ARGS__)) \
65 } \
66 }; \
67 \
68 template<> \
69 struct FieldAccess<sname> \
70 { \
71 template<size_t Idx> \
72 constexpr static const auto& Get (const sname& s) \
73 { \
74 BOOST_PP_REPEAT(BOOST_PP_TUPLE_SIZE((__VA_ARGS__)), ORAL_GET_FIELD, (__VA_ARGS__)) \
75 } \
76 \
77 template<size_t Idx> \
78 constexpr static auto& Get (sname& s) \
79 { \
80 BOOST_PP_REPEAT(BOOST_PP_TUPLE_SIZE((__VA_ARGS__)), ORAL_GET_FIELD, (__VA_ARGS__)) \
81 } \
82 }; \
83 \
84 template<> \
85 struct FieldIndexAccess<sname> \
86 { \
87 template<auto Ptr> \
88 constexpr static size_t FieldIndex (); \
89 }; \
90 \
91 BOOST_PP_REPEAT(BOOST_PP_TUPLE_SIZE((__VA_ARGS__)), ORAL_GET_FIELD_INDEX, (sname, (__VA_ARGS__))) \
92} \
93
94#endif
95
96namespace LC::Util::oral
97{
98 using QSqlQuery_ptr = std::shared_ptr<QSqlQuery>;
99
100 class QueryException : public std::runtime_error
101 {
102 const QSqlQuery Query_;
103 public:
104 QueryException (const std::string& str, const QSqlQuery_ptr& q)
105 : QueryException { str, *q }
106 {
107 }
108
109 QueryException (const std::string& str, const QSqlQuery& q)
110 : std::runtime_error { str }
111 , Query_ { q }
112 {
113 }
114
115 ~QueryException () noexcept = default;
116
117 const QSqlQuery& GetQuery () const
118 {
119 return Query_;
120 }
121 };
122
123 template<typename>
124 constexpr size_t SeqSize = -1;
125
126 template<typename>
127 struct MemberNames {};
128
129 template<typename>
130 struct FieldAccess {};
131
132 template<typename>
134
135 namespace detail
136 {
137 template<size_t Idx, typename Seq>
138 constexpr decltype (auto) Get (const Seq& seq)
139 {
141 }
142
143 template<size_t Idx, typename Seq>
144 constexpr decltype (auto) Get (Seq& seq)
145 {
147 }
148
149 template<typename T, CtString str>
150 consteval auto MorphFieldName ()
151 {
152 if constexpr (requires { T::template FieldNameMorpher<str> (); })
153 return T::template FieldNameMorpher<str> ();
154 else if constexpr (str.EndsWith ('_'))
155 return str.template Chop<1> ();
156 else
157 return str;
158 }
159
160 template<typename Seq, int Idx>
161 consteval auto GetFieldName ()
162 {
163 constexpr auto str = MemberNames<Seq>::template Get<Idx> ();
164 return MorphFieldName<Seq, str> ();
165 }
166
167 template<typename S>
168 constexpr auto SeqIndices = std::make_index_sequence<SeqSize<S>> {};
169
170 template<typename S>
171 constexpr auto FieldNames = []<size_t... Ix> (std::index_sequence<Ix...>) constexpr
172 {
173 return std::tuple { GetFieldName<S, Ix> ()... };
174 } (SeqIndices<S>);
175
176 template<typename S>
177 constexpr auto BoundFieldNames = []<size_t... Ix> (std::index_sequence<Ix...>) constexpr
178 {
179 return std::tuple { (":" + GetFieldName<S, Ix> ())... };
180 } (SeqIndices<S>);
181
182 template<typename S>
183 constexpr auto QualifiedFieldNames = []<size_t... Ix> (std::index_sequence<Ix...>) constexpr
184 {
185 return std::tuple { (S::ClassName + "." + GetFieldName<S, Ix> ())... };
186 } (SeqIndices<S>);
187
188 template<auto Ptr>
189 constexpr size_t FieldIndex () noexcept
190 {
191 using S = MemberPtrStruct_t<Ptr>;
193 }
194
195 template<auto Ptr>
196 constexpr auto GetFieldNamePtr () noexcept
197 {
198 using S = MemberPtrStruct_t<Ptr>;
200 }
201
202 template<auto Ptr>
203 constexpr auto GetQualifiedFieldNamePtr () noexcept
204 {
205 using S = MemberPtrStruct_t<Ptr>;
206 return S::ClassName + "." + GetFieldName<S, FieldIndex<Ptr> ()> ();
207 }
208
209 template<typename T>
210 concept TypeNameCustomized = requires { typename T::TypeName; };
211
212 template<typename T>
213 concept BaseTypeCustomized = requires { typename T::BaseType; };
214 }
215
216 template<typename ImplFactory, typename T, typename = void>
218 {
219 constexpr auto operator() () const noexcept
220 {
221 if constexpr (HasType<T> (Typelist<int, qlonglong, qulonglong, bool> {}) || std::is_enum_v<T>)
222 return "INTEGER"_ct;
223 else if constexpr (std::is_same_v<T, double>)
224 return "REAL"_ct;
225 else if constexpr (std::is_same_v<T, QString> || std::is_same_v<T, QDateTime> || std::is_same_v<T, QUrl>)
226 return "TEXT"_ct;
227 else if constexpr (std::is_same_v<T, QByteArray>)
228 return ImplFactory::TypeLits::Binary;
229 else if constexpr (detail::TypeNameCustomized<T>)
230 return T::TypeName;
231 else if constexpr (detail::BaseTypeCustomized<T>)
233 else
234 static_assert (std::is_same_v<T, struct Dummy>, "Unsupported type");
235 }
236 };
237
238 template<typename ImplFactory, typename T>
239 struct Type2Name<ImplFactory, Unique<T>>
240 {
241 constexpr auto operator() () const noexcept { return Type2Name<ImplFactory, T> {} () + " UNIQUE"; }
242 };
243
244 template<typename ImplFactory, typename T>
245 struct Type2Name<ImplFactory, NotNull<T>>
246 {
247 constexpr auto operator() () const noexcept { return Type2Name<ImplFactory, T> {} () + " NOT NULL"; }
248 };
249
250 template<typename ImplFactory, typename T, typename... Tags>
251 struct Type2Name<ImplFactory, PKey<T, Tags...>>
252 {
253 constexpr auto operator() () const noexcept { return Type2Name<ImplFactory, T> {} () + " PRIMARY KEY"; }
254 };
255
256 template<typename ImplFactory, typename... Tags>
257 struct Type2Name<ImplFactory, PKey<int, Tags...>>
258 {
259 constexpr auto operator() () const noexcept { return ImplFactory::TypeLits::IntAutoincrement; }
260 };
261
262 template<typename ImplFactory, auto Ptr>
263 struct Type2Name<ImplFactory, References<Ptr>>
264 {
265 constexpr auto operator() () const noexcept
266 {
267 constexpr auto className = MemberPtrStruct_t<Ptr>::ClassName;
269 " REFERENCES " + className + " (" + detail::GetFieldNamePtr<Ptr> () + ") ON DELETE CASCADE";
270 }
271 };
272
273 template<typename T, typename = void>
275 {
276 QVariant operator() (const T& t) const noexcept
277 {
278 if constexpr (std::is_same_v<T, QDateTime>)
279 return t.toString (Qt::ISODate);
280 else if constexpr (std::is_enum_v<T>)
281 return static_cast<qint64> (t);
282 else if constexpr (IsIndirect<T> {})
284 else if constexpr (detail::TypeNameCustomized<T>)
285 return t.ToVariant ();
286 else if constexpr (detail::BaseTypeCustomized<T>)
287 return ToVariant<typename T::BaseType> {} (t.ToBaseType ());
288 else
289 return t;
290 }
291 };
292
293 template<typename T, typename = void>
295 {
296 T operator() (const QVariant& var) const noexcept
297 {
298 if constexpr (std::is_same_v<T, QDateTime>)
299 return QDateTime::fromString (var.toString (), Qt::ISODate);
300 else if constexpr (std::is_enum_v<T>)
301 return static_cast<T> (var.value<qint64> ());
302 else if constexpr (IsIndirect<T> {})
304 else if constexpr (detail::TypeNameCustomized<T>)
305 return T::FromVariant (var);
306 else if constexpr (detail::BaseTypeCustomized<T>)
307 return T::FromBaseType (FromVariant<typename T::BaseType> {} (var));
308 else
309 return var.value<T> ();
310 }
311 };
312
313 namespace detail
314 {
315 template<typename Seq, int Idx>
316 using ValueAtC_t = std::decay_t<decltype (Get<Idx> (std::declval<Seq> ()))>;
317
318 template<typename T>
319 struct IsPKey : std::false_type {};
320
321 template<typename U, typename... Tags>
322 struct IsPKey<PKey<U, Tags...>> : std::true_type {};
323
324 template<typename T>
325 QVariant ToVariantF (const T& t) noexcept
326 {
327 return ToVariant<T> {} (t);
328 }
329
330 template<size_t Ix, typename Seq>
331 void BindAtIndex (const Seq& seq, QSqlQuery& query, bool bindPrimaryKey)
332 {
333 if (bindPrimaryKey || !IsPKey<ValueAtC_t<Seq, Ix>>::value)
334 query.bindValue (ToString<std::get<Ix> (BoundFieldNames<Seq>)> (), ToVariantF (Get<Ix> (seq)));
335 }
336
337 template<typename Seq>
338 auto DoInsert (const Seq& seq, QSqlQuery& insertQuery, bool bindPrimaryKey)
339 {
340 [&]<size_t... Ix> (std::index_sequence<Ix...>)
341 {
342 (BindAtIndex<Ix> (seq, insertQuery, bindPrimaryKey), ...);
343 } (SeqIndices<Seq>);
344
345 if (!insertQuery.exec ())
346 {
347 qCritical () << "insert query execution failed";
348 DBLock::DumpError (insertQuery);
349 throw QueryException ("insert query execution failed", insertQuery);
350 }
351 }
352
353 template<typename Seq>
354 consteval int PKeyIndexUnsafe ()
355 {
356 auto run = []<size_t... Idxes> (std::index_sequence<Idxes...>)
357 {
358 int result = -1;
359 ((IsPKey<ValueAtC_t<Seq, Idxes>>::value ? (result = Idxes) : 0), ...);
360 return result;
361 };
362 return run (SeqIndices<Seq>);
363 }
364
365 template<typename Seq>
366 consteval int PKeyIndex ()
367 {
368 const auto idx = PKeyIndexUnsafe<Seq> ();
369 static_assert (idx >= 0);
370 return idx;
371 }
372
373 template<typename Seq>
374 constexpr int PKeyIndex_v = PKeyIndex<Seq> ();
375
376 template<typename Seq>
377 concept HasPKey = PKeyIndexUnsafe<Seq> () >= 0;
378
379 template<typename Seq>
380 constexpr auto HasAutogenPKey () noexcept
381 {
382 if constexpr (HasPKey<Seq>)
384 else
385 return false;
386 }
387
388 template<typename Seq>
389 constexpr auto ExtractConflictingFields (InsertAction::Replace::PKeyType)
390 {
391 return std::tuple { detail::GetFieldName<Seq, PKeyIndex_v<Seq>> () };
392 }
393
394 template<typename Seq, auto... Ptrs>
396 {
397 return std::tuple { std::get<FieldIndex<Ptrs> ()> (FieldNames<Seq>)... };
398 }
399
400 template<typename Seq>
402 {
403 const QSqlDatabase DB_;
404 constexpr static bool HasAutogen_ = HasAutogenPKey<Seq> ();
405 public:
406 AdaptInsert (const QSqlDatabase& db) noexcept
407 : DB_ { db }
408 {
409 }
410
411 template<typename Action = InsertAction::DefaultTag>
412 auto operator() (Seq& t, Action action = {}) const
413 {
414 return Run<SQLite::ImplFactory> (t, action);
415 }
416
417 template<typename ImplFactory>
418 auto operator() (ImplFactory, Seq& t, auto action) const
419 {
420 return Run<ImplFactory> (t, action);
421 }
422
423 template<typename Action = InsertAction::DefaultTag>
424 auto operator() (const Seq& t, Action action = {}) const
425 {
426 return Run<SQLite::ImplFactory> (t, action);
427 }
428
429 template<typename ImplFactory>
430 auto operator() (ImplFactory, const Seq& t, auto action) const
431 {
432 return Run<ImplFactory> (t, action);
433 }
434 private:
435 template<typename ImplFactory, typename Action>
436 constexpr static auto MakeInsertSuffix (Action action)
437 {
438 if constexpr (std::is_same_v<Action, InsertAction::DefaultTag> || std::is_same_v<Action, InsertAction::IgnoreTag>)
439 return ImplFactory::GetInsertSuffix (action);
440 else
441 return ImplFactory::GetInsertSuffix (InsertAction::Replace {},
444 }
445
446 template<typename ImplFactory>
447 constexpr static auto MakeQueryForAction (auto action)
448 {
449 return ImplFactory::GetInsertPrefix (action) +
450 " INTO " + Seq::ClassName +
451 " (" + JoinTup (FieldNames<Seq>, ", ") + ") " +
452 "VALUES (" + JoinTup (BoundFieldNames<Seq>, ", ") + ") " +
453 MakeInsertSuffix<ImplFactory> (action);
454 }
455
456 template<typename ImplFactory, typename T>
457 auto Run (T& t, auto action) const
458 {
459 QSqlQuery query { DB_ };
460 constexpr auto queryText = MakeQueryForAction<ImplFactory> (action);
461 query.prepare (ToString<queryText> ());
462
463 DoInsert (t, query, !HasAutogen_);
464
465 if constexpr (HasAutogen_)
466 {
467 constexpr auto index = PKeyIndex_v<Seq>;
468
469 const auto& lastId = FromVariant<ValueAtC_t<Seq, index>> {} (query.lastInsertId ());
470 if constexpr (!std::is_const_v<T>)
471 Get<index> (t) = lastId;
472 else
473 return lastId;
474 }
475 }
476 };
477
478 template<typename Seq>
480 {
481 QSqlQuery DeleteQuery_;
482 public:
483 AdaptDelete (const QSqlDatabase& db) noexcept
484 : DeleteQuery_ { db }
485 {
486 if constexpr (HasPKey<Seq>)
487 {
488 constexpr auto index = PKeyIndex_v<Seq>;
489 constexpr auto del = "DELETE FROM " + Seq::ClassName +
490 " WHERE " + std::get<index> (FieldNames<Seq>) + " = ?";
491 DeleteQuery_.prepare (ToString<del> ());
492 }
493 }
494
495 void operator() (const Seq& seq) requires HasPKey<Seq>
496 {
497 constexpr auto index = PKeyIndex_v<Seq>;
498 DeleteQuery_.bindValue (0, ToVariantF (Get<index> (seq)));
499 if (!DeleteQuery_.exec ())
500 throw QueryException ("delete query execution failed", DeleteQuery_);
501 }
502 };
503
504 template<typename T, size_t... Indices>
505 T InitializeFromQuery (const QSqlQuery& q, std::index_sequence<Indices...>, int startIdx) noexcept
506 {
507 if constexpr (requires { T { FromVariant<ValueAtC_t<T, Indices>> {} (QVariant {})... }; })
508 return T { FromVariant<ValueAtC_t<T, Indices>> {} (q.value (startIdx + Indices))... };
509 else
510 {
511 T t;
512 ((Get<Indices> (t) = FromVariant<ValueAtC_t<T, Indices>> {} (q.value (startIdx + Indices))), ...);
513 return t;
514 }
515 }
516
536
537 template<ExprType Type>
538 constexpr auto TypeToSql () noexcept
539 {
540 if constexpr (Type == ExprType::Greater)
541 return ">"_ct;
542 else if constexpr (Type == ExprType::Less)
543 return "<"_ct;
544 else if constexpr (Type == ExprType::Equal)
545 return "="_ct;
546 else if constexpr (Type == ExprType::Geq)
547 return ">="_ct;
548 else if constexpr (Type == ExprType::Leq)
549 return "<="_ct;
550 else if constexpr (Type == ExprType::Neq)
551 return "!="_ct;
552 else if constexpr (Type == ExprType::Like)
553 return "LIKE"_ct;
554 else if constexpr (Type == ExprType::And)
555 return "AND"_ct;
556 else if constexpr (Type == ExprType::Or)
557 return "OR"_ct;
558 else
559 static_assert (std::is_same_v<struct D1, ExprType>, "Invalid expression type");
560 }
561
562 constexpr bool IsRelational (ExprType type) noexcept
563 {
564 return type == ExprType::Greater ||
565 type == ExprType::Less ||
566 type == ExprType::Equal ||
567 type == ExprType::Geq ||
568 type == ExprType::Leq ||
569 type == ExprType::Neq ||
570 type == ExprType::Like;
571 }
572
573 template<typename T>
575 {
576 using value_type = T;
577 };
578
579 template<typename T>
580 using UnwrapIndirect_t = typename std::conditional_t<IsIndirect<T> {},
581 T,
582 WrapDirect<T>>::value_type;
583
584 template<ExprType Type, typename Seq, typename L, typename R>
585 constexpr bool Typecheck ()
586 {
587 if constexpr (IsRelational (Type))
588 {
591 return requires (LReal l, RReal r) { l == r; };
592 }
593 else
594 return true;
595 }
596
597 template<ExprType Type, typename L = void, typename R = void>
598 class ExprTree;
599
600 template<typename T>
601 struct IsExprTree : std::false_type {};
602
603 template<ExprType Type, typename L, typename R>
604 struct IsExprTree<ExprTree<Type, L, R>> : std::true_type {};
605
606 template<typename L, typename R>
608 {
609 L Left_;
610 R Right_;
611 public:
612 constexpr AssignList (const L& l, const R& r) noexcept
613 : Left_ { l }
614 , Right_ { r }
615 {
616 }
617
618 template<typename Seq, CtString S>
619 constexpr static auto ToSql () noexcept
620 {
621 if constexpr (IsExprTree<L> {})
622 return L::GetFieldName () + " = " + R::template ToSql<Seq, S + "r"> ();
623 else
624 return L::template ToSql<Seq, S + "l"> () + ", " + R::template ToSql<Seq, S + "r"> ();
625 }
626
627 template<typename Seq, CtString S>
628 void BindValues (QSqlQuery& query) const noexcept
629 {
630 Left_.template BindValues<Seq, S + "l"> (query);
631 Right_.template BindValues<Seq, S + "r"> (query);
632 }
633
634 template<typename OL, typename OR>
635 constexpr auto operator, (const AssignList<OL, OR>& tail) noexcept
636 {
637 return AssignList<AssignList<L, R>, AssignList<OL, OR>> { *this, tail };
638 }
639 };
640
641 template<ExprType Type, typename L, typename R>
643 {
644 L Left_;
645 R Right_;
646 public:
647 constexpr ExprTree (const L& l, const R& r) noexcept
648 : Left_ (l)
649 , Right_ (r)
650 {
651 }
652
653 template<typename Seq, CtString S>
654 constexpr static auto ToSql () noexcept
655 {
656 static_assert (Typecheck<Type, Seq, L, R> (),
657 "Incompatible types passed to a relational operator.");
658
659 return L::template ToSql<Seq, S + "l"> () + " " + TypeToSql<Type> () + " " + R::template ToSql<Seq, S + "r"> ();
660 }
661
662 template<typename Seq, CtString S>
663 void BindValues (QSqlQuery& query) const noexcept
664 {
665 Left_.template BindValues<Seq, S + "l"> (query);
666 Right_.template BindValues<Seq, S + "r"> (query);
667 }
668
669 template<typename T>
670 constexpr static auto AdditionalTables () noexcept
671 {
672 return std::tuple_cat (L::template AdditionalTables<T> (), R::template AdditionalTables<T> ());
673 }
674
675 template<typename T>
676 constexpr static bool HasAdditionalTables () noexcept
677 {
678 return L::template HasAdditionalTables<T> () || R::template HasAdditionalTables<T> ();
679 }
680 };
681
682 template<typename T>
683 class ExprTree<ExprType::LeafData, T, void>
684 {
685 const T& Data_;
686 public:
687 template<typename>
688 using ValueType_t = T;
689
690 constexpr ExprTree (const T& t) noexcept
691 : Data_ (t)
692 {
693 }
694
695 template<typename, CtString S>
696 constexpr static auto ToSql () noexcept
697 {
698 return ":bound_" + S;
699 }
700
701 template<typename Seq, CtString S>
702 void BindValues (QSqlQuery& query) const noexcept
703 {
704 constexpr auto varName = ToSql<Seq, S> ();
705 query.bindValue (ToString<varName> (), ToVariantF (Data_));
706 }
707
708 template<typename>
709 constexpr static auto AdditionalTables () noexcept
710 {
711 return std::tuple {};
712 }
713
714 template<typename>
715 constexpr static bool HasAdditionalTables () noexcept
716 {
717 return false;
718 }
719 };
720
721 template<typename T>
722 constexpr auto AsLeafData (const T& node) noexcept
723 {
724 if constexpr (IsExprTree<T> {})
725 return node;
726 else
727 return ExprTree<ExprType::LeafData, T> { node };
728 }
729
730 template<auto... Ptr>
731 struct MemberPtrs {};
732
733 template<auto Ptr>
735 {
736 using ExpectedType_t = MemberPtrType_t<Ptr>;
737 public:
738 template<typename>
739 using ValueType_t = ExpectedType_t;
740
741 template<typename Seq, CtString S>
742 constexpr static auto ToSql () noexcept
743 {
745 }
746
747 template<typename Seq, CtString S>
748 void BindValues (QSqlQuery&) const noexcept
749 {
750 }
751
752 constexpr static auto GetFieldName () noexcept
753 {
755 }
756
757 template<typename T>
758 constexpr static auto AdditionalTables () noexcept
759 {
760 using Seq = MemberPtrStruct_t<Ptr>;
761 if constexpr (std::is_same_v<Seq, T>)
762 return std::tuple {};
763 else
764 return std::tuple { Seq::ClassName };
765 }
766
767 template<typename T>
768 constexpr static bool HasAdditionalTables () noexcept
769 {
770 return !std::is_same_v<MemberPtrStruct_t<Ptr>, T>;
771 }
772
773 constexpr auto operator= (const ExpectedType_t& r) const noexcept
774 {
775 return AssignList { *this, AsLeafData (r) };
776 }
777 };
778
779 template<>
780 class ExprTree<ExprType::ConstTrue, void, void>
781 {
782 public:
783 template<typename, CtString>
784 constexpr static auto ToSql () noexcept
785 {
786 return "1 = 1"_ct;
787 }
788
789 template<typename, CtString>
790 void BindValues (QSqlQuery&) const noexcept
791 {
792 }
793
794 template<typename>
795 constexpr static bool HasAdditionalTables () noexcept
796 {
797 return false;
798 }
799 };
800
802
803 template<ExprType Type, typename L, typename R>
804 auto MakeExprTree (const L& left, const R& right) noexcept
805 {
806 using EL = decltype (AsLeafData (left));
807 using ER = decltype (AsLeafData (right));
808 return ExprTree<Type, EL, ER> { AsLeafData (left), AsLeafData (right) };
809 }
810
811 template<typename L, typename R>
812 constexpr bool EitherIsExprTree () noexcept
813 {
814 if (IsExprTree<L> {})
815 return true;
816 if (IsExprTree<R> {})
817 return true;
818 return false;
819 }
820
821 template<typename L, typename R>
822 using EnableRelOp_t = std::enable_if_t<EitherIsExprTree<L, R> ()>;
823
824 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
825 auto operator< (const L& left, const R& right) noexcept
826 {
827 return MakeExprTree<ExprType::Less> (left, right);
828 }
829
830 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
831 auto operator> (const L& left, const R& right) noexcept
832 {
833 return MakeExprTree<ExprType::Greater> (left, right);
834 }
835
836 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
837 auto operator== (const L& left, const R& right) noexcept
838 {
839 return MakeExprTree<ExprType::Equal> (left, right);
840 }
841
842 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
843 auto operator!= (const L& left, const R& right) noexcept
844 {
845 return MakeExprTree<ExprType::Neq> (left, right);
846 }
847
848 template<ExprType Op>
849 struct InfixBinary {};
850 }
851
852 namespace infix
853 {
855 }
856
857 namespace detail
858 {
859 template<typename L, ExprType Op>
861 {
862 const L& Left_;
863 };
864
865 template<typename L, ExprType Op>
866 auto operator| (const L& left, InfixBinary<Op>) noexcept
867 {
868 return InfixBinaryProxy<L, Op> { left };
869 }
870
871 template<typename L, ExprType Op, typename R>
872 auto operator| (const InfixBinaryProxy<L, Op>& left, const R& right) noexcept
873 {
874 return MakeExprTree<Op> (left.Left_, right);
875 }
876
877 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
878 auto operator&& (const L& left, const R& right) noexcept
879 {
880 return MakeExprTree<ExprType::And> (left, right);
881 }
882
883 template<typename L, typename R, typename = EnableRelOp_t<L, R>>
884 auto operator|| (const L& left, const R& right) noexcept
885 {
886 return MakeExprTree<ExprType::Or> (left, right);
887 }
888
889 template<CtString BindPrefix, typename Seq, typename Tree>
890 constexpr auto ExprTreeToSql () noexcept
891 {
892 return Tree::template ToSql<Seq, BindPrefix> ();
893 }
894
895 template<CtString BindPrefix, typename Seq, typename Tree>
896 void BindExprTree (const Tree& tree, QSqlQuery& query)
897 {
898 tree.template BindValues<Seq, BindPrefix> (query);
899 }
900
902 {
906 };
907
908 template<AggregateFunction, auto Ptr>
909 struct AggregateType {};
910
911 struct CountAll {};
912
913 inline constexpr CountAll *CountAllPtr = nullptr;
914
915 template<typename... MemberDirectionList>
916 struct OrderBy {};
917
918 template<auto... Ptrs>
919 struct GroupBy {};
920
921 struct SelectWhole {};
922
923 template<typename L, typename R>
924 struct SelectorUnion {};
925
926 template<typename T>
927 struct IsSelector : std::false_type {};
928
929 template<>
930 struct IsSelector<SelectWhole> : std::true_type {};
931
932 template<AggregateFunction Fun, auto Ptr>
933 struct IsSelector<AggregateType<Fun, Ptr>> : std::true_type {};
934
935 template<auto... Ptrs>
936 struct IsSelector<MemberPtrs<Ptrs...>> : std::true_type {};
937
938 template<typename L, typename R>
939 struct IsSelector<SelectorUnion<L, R>> : std::true_type {};
940
941 template<typename L, typename R, typename = std::enable_if_t<IsSelector<L> {} && IsSelector<R> {}>>
943 {
944 return {};
945 }
946 }
947
948 namespace sph
949 {
950 template<auto Ptr>
952
953 template<auto... Ptrs>
954 constexpr detail::MemberPtrs<Ptrs...> fields {};
955
957
958 template<auto... Ptrs>
959 struct asc {};
960
961 template<auto... Ptrs>
962 struct desc {};
963
964 template<auto Ptr = detail::CountAllPtr>
966
967 template<auto Ptr>
969
970 template<auto Ptr>
972 };
973
974 template<typename... Orders>
975 constexpr detail::OrderBy<Orders...> OrderBy {};
976
977 template<auto... Ptrs>
978 constexpr detail::GroupBy<Ptrs...> GroupBy {};
979
980 struct Limit
981 {
982 uint64_t Count;
983 };
984
985 struct Offset
986 {
987 uint64_t Count;
988 };
989
990 namespace detail
991 {
992 template<auto Ptr>
993 auto MemberFromVariant (const QVariant& var) noexcept
994 {
996 }
997
998 template<auto Ptr>
999 auto MakeIndexedQueryHandler (const QSqlQuery& q, int startIdx = 0) noexcept
1000 {
1001 return MemberFromVariant<Ptr> (q.value (startIdx));
1002 }
1003
1004 template<auto... Ptrs>
1005 auto MakeIndexedQueryHandler (MemberPtrs<Ptrs...>, const QSqlQuery& q, int startIdx) noexcept
1006 {
1007 if constexpr (sizeof... (Ptrs) == 1)
1008 return MakeIndexedQueryHandler<Ptrs...> (q, startIdx);
1009 else
1010 return [&]<size_t... Ix> (std::index_sequence<Ix...>)
1011 {
1012 return std::tuple { MemberFromVariant<Ptrs> (q.value (startIdx + Ix))... };
1013 } (std::make_index_sequence<sizeof... (Ptrs)> {});
1014 }
1015
1016 enum class SelectBehaviour { Some, One };
1017
1018 struct OrderNone {};
1019 struct GroupNone {};
1020 struct LimitNone {};
1021 struct OffsetNone {};
1022
1023 template<size_t RepIdx, size_t TupIdx, typename Tuple, typename NewType>
1024 constexpr decltype (auto) GetReplaceTupleElem (Tuple&& tuple, NewType&& arg) noexcept
1025 {
1026 if constexpr (RepIdx == TupIdx)
1027 return std::forward<NewType> (arg);
1028 else
1029 return std::get<TupIdx> (tuple);
1030 }
1031
1032 template<size_t RepIdx, typename NewType, typename Tuple, size_t... TupIdxs>
1033 constexpr auto ReplaceTupleElemImpl (Tuple&& tuple, NewType&& arg, std::index_sequence<TupIdxs...>) noexcept
1034 {
1035 return std::tuple
1036 {
1037 GetReplaceTupleElem<RepIdx, TupIdxs> (std::forward<Tuple> (tuple), std::forward<NewType> (arg))...
1038 };
1039 }
1040
1041 template<size_t RepIdx, typename NewType, typename... TupleArgs>
1042 constexpr auto ReplaceTupleElem (std::tuple<TupleArgs...>&& tuple, NewType&& arg) noexcept
1043 {
1044 return ReplaceTupleElemImpl<RepIdx> (std::move (tuple),
1045 std::forward<NewType> (arg),
1046 std::index_sequence_for<TupleArgs...> {});
1047 }
1048
1049 template<typename Seq, typename T>
1051 {
1052 constexpr static int Value = 1;
1053 };
1054
1055 template<typename Seq, typename... Args>
1056 struct DetectShift<Seq, std::tuple<Args...>>
1057 {
1058 constexpr static int Value = (DetectShift<Seq, Args>::Value + ...);
1059 };
1060
1061 template<typename Seq>
1062 struct DetectShift<Seq, Seq>
1063 {
1064 constexpr static int Value = SeqSize<Seq>;
1065 };
1066
1067 template<typename... LArgs, typename... RArgs>
1068 auto Combine (std::tuple<LArgs...>&& left, std::tuple<RArgs...>&& right) noexcept
1069 {
1070 return std::tuple_cat (std::move (left), std::move (right));
1071 }
1072
1073 template<typename... LArgs, typename R>
1074 auto Combine (std::tuple<LArgs...>&& left, const R& right) noexcept
1075 {
1076 return std::tuple_cat (std::move (left), std::tuple { right });
1077 }
1078
1079 template<typename L, typename... RArgs>
1080 auto Combine (const L& left, std::tuple<RArgs...>&& right) noexcept
1081 {
1082 return std::tuple_cat (std::tuple { left }, std::move (right));
1083 }
1084
1085 template<typename L, typename R>
1086 auto Combine (const L& left, const R& right) noexcept
1087 {
1088 return std::tuple { left, right };
1089 }
1090
1092 {
1095 };
1096
1097 template<ResultBehaviour ResultBehaviour, typename ResList>
1098 decltype (auto) HandleResultBehaviour (ResList&& list) noexcept
1099 {
1100 if constexpr (ResultBehaviour == ResultBehaviour::All)
1101 return std::forward<ResList> (list);
1102 else if constexpr (ResultBehaviour == ResultBehaviour::First)
1103 return list.value (0);
1104 }
1105
1106 template<typename F, typename R>
1113
1114 template<typename F, typename R>
1116
1118 {
1119 const QSqlDatabase DB_;
1120 public:
1121 SelectWrapperCommon (const QSqlDatabase& db) noexcept
1122 : DB_ { db }
1123 {
1124 }
1125 protected:
1126 auto RunQuery (const QString& queryStr,
1127 auto&& binder) const
1128 {
1129 QSqlQuery query { DB_ };
1130 query.prepare (queryStr);
1131 binder (query);
1132
1133 if (!query.exec ())
1134 {
1135 qCritical () << "select query execution failed";
1136 DBLock::DumpError (query);
1137 throw QueryException ("fetch query execution failed", std::make_shared<QSqlQuery> (query));
1138 }
1139
1140 return query;
1141 }
1142 };
1143
1144 template<typename L, typename O>
1145 constexpr auto LimitOffsetToString () noexcept
1146 {
1147 if constexpr (std::is_same_v<L, LimitNone>)
1148 {
1149 static_assert (std::is_same_v<O, OffsetNone>, "LIMIT-less queries currently cannot have OFFSET");
1150 return ""_ct;
1151 }
1152 else
1153 return " LIMIT :limit "_ct +
1154 [] () constexpr
1155 {
1156 if constexpr (std::is_same_v<O, OffsetNone>)
1157 return ""_ct;
1158 else
1159 return " OFFSET :offset"_ct;
1160 } ();
1161 }
1162
1163 template<typename L, typename O>
1164 void BindLimitOffset (QSqlQuery& query, L limit, O offset) noexcept
1165 {
1166 if constexpr (!std::is_same_v<std::decay_t<L>, LimitNone>)
1167 query.bindValue (":limit", qulonglong { limit.Count });
1168 if constexpr (!std::is_same_v<std::decay_t<O>, OffsetNone>)
1169 query.bindValue (":offset", qulonglong { offset.Count });
1170 }
1171
1172 template<typename T, typename Selector>
1174
1175 struct HSBaseAll { constexpr inline static auto ResultBehaviour_v = ResultBehaviour::All; };
1176
1177 struct HSBaseFirst { constexpr inline static auto ResultBehaviour_v = ResultBehaviour::First; };
1178
1179 template<typename T>
1181 {
1182 constexpr inline static auto Fields = JoinTup (QualifiedFieldNames<T>, ", "_ct);
1183
1184 static auto Initializer (const QSqlQuery& q, int startIdx)
1185 {
1186 return InitializeFromQuery<T> (q, SeqIndices<T>, startIdx);
1187 }
1188 };
1189
1190 template<typename T, auto... Ptrs>
1192 {
1193 private:
1194 template<size_t... Ixs>
1195 constexpr static auto SelectFields ()
1196 {
1197 return std::tuple { std::get<Ixs> (QualifiedFieldNames<T>)... };
1198 }
1199 public:
1200 constexpr inline static auto Fields = JoinTup (SelectFields<FieldIndex<Ptrs> ()...> (), ", ");
1201
1202 static auto Initializer (const QSqlQuery& q, int startIdx) noexcept
1203 {
1204 return MakeIndexedQueryHandler (MemberPtrs<Ptrs...> {}, q, startIdx);
1205 }
1206 };
1207
1208 template<typename T>
1210 {
1211 constexpr inline static auto Fields = "count(1)"_ct;
1212
1213 static auto Initializer (const QSqlQuery& q, int startIdx)
1214 {
1215 return q.value (startIdx).toLongLong ();
1216 }
1217 };
1218
1219 template<typename T, auto Ptr>
1221 {
1222 constexpr inline static auto Fields = "count(" + GetQualifiedFieldNamePtr<Ptr> () + ")";
1223
1224 static auto Initializer (const QSqlQuery& q, int startIdx)
1225 {
1226 return q.value (startIdx).toLongLong ();
1227 }
1228 };
1229
1230 template<CtString Aggregate, typename T, auto Ptr>
1232 {
1233 constexpr inline static auto Fields = Aggregate + "(" + GetQualifiedFieldNamePtr<Ptr> () + ")";
1234
1235 static auto Initializer (const QSqlQuery& q, int startIdx) noexcept
1236 {
1237 return MakeIndexedQueryHandler<Ptr> (q, startIdx);
1238 }
1239 };
1240
1241 template<typename T, auto Ptr>
1242 struct HandleSelector<T, AggregateType<AggregateFunction::Min, Ptr>> : HandleAggSelector<"min"_ct, T, Ptr> {};
1243
1244 template<typename T, auto Ptr>
1245 struct HandleSelector<T, AggregateType<AggregateFunction::Max, Ptr>> : HandleAggSelector<"max"_ct, T, Ptr> {};
1246
1247 constexpr auto CombineBehaviour (ResultBehaviour l, ResultBehaviour r) noexcept
1248 {
1251 return ResultBehaviour::All;
1252 }
1253
1254 template<typename T, typename L, typename R>
1256 {
1259
1260 constexpr inline static auto ResultBehaviour_v = CombineBehaviour (HL::ResultBehaviour_v, HR::ResultBehaviour_v);
1261
1262 constexpr inline static auto Fields = HL::Fields + ", " + HR::Fields;
1263
1264 static auto Initializer (const QSqlQuery& q, int startIdx) noexcept
1265 {
1266 constexpr auto shift = DetectShift<T, decltype (HL::Initializer (q, 0))>::Value;
1267 return Combine (HL::Initializer (q, startIdx), HR::Initializer (q, startIdx + shift));
1268 }
1269 };
1270
1271 template<typename T, SelectBehaviour SelectBehaviour>
1273 {
1274 template<typename ParamsTuple>
1275 struct Builder
1276 {
1277 const SelectWrapper& W_;
1278 ParamsTuple Params_;
1279
1280 template<typename NewTuple>
1281 constexpr auto RepTuple (NewTuple&& tuple) noexcept
1282 {
1283 return Builder<NewTuple> { W_, tuple };
1284 }
1285
1286 template<typename U>
1287 constexpr auto Select (U&& selector) && noexcept
1288 {
1289 return RepTuple (ReplaceTupleElem<0> (std::move (Params_), std::forward<U> (selector)));
1290 }
1291
1292 template<typename U>
1293 constexpr auto Where (U&& tree) && noexcept
1294 {
1295 return RepTuple (ReplaceTupleElem<1> (std::move (Params_), std::forward<U> (tree)));
1296 }
1297
1298 template<typename U>
1299 constexpr auto Order (U&& order) && noexcept
1300 {
1301 return RepTuple (ReplaceTupleElem<2> (std::move (Params_), std::forward<U> (order)));
1302 }
1303
1304 template<typename U>
1305 constexpr auto Group (U&& group) && noexcept
1306 {
1307 return RepTuple (ReplaceTupleElem<3> (std::move (Params_), std::forward<U> (group)));
1308 }
1309
1310 constexpr auto Limit (Limit limit) && noexcept
1311 {
1312 return RepTuple (ReplaceTupleElem<4> (std::move (Params_), limit));
1313 }
1314
1315 constexpr auto Limit (uint64_t limit) && noexcept
1316 {
1317 return std::move (*this).Limit (oral::Limit { limit });
1318 }
1319
1320 constexpr auto Offset (Offset offset) && noexcept
1321 {
1322 return RepTuple (ReplaceTupleElem<5> (std::move (Params_), offset));
1323 }
1324
1325 constexpr auto Offset (uint64_t offset) && noexcept
1326 {
1327 return std::move (*this).Offset (oral::Offset { offset });
1328 }
1329
1330 auto operator() () &&
1331 {
1332 return std::apply (W_, Params_);
1333 }
1334
1335 template<auto... Ptrs>
1336 constexpr auto Group () && noexcept
1337 {
1338 return std::move (*this).Group (GroupBy<Ptrs...> {});
1339 }
1340 };
1341 public:
1343
1344 auto Build () const noexcept
1345 {
1346 std::tuple defParams
1347 {
1348 SelectWhole {},
1350 OrderNone {},
1351 GroupNone {},
1352 LimitNone {},
1353 OffsetNone {}
1354 };
1355 return Builder<decltype (defParams)> { *this, defParams };
1356 }
1357
1358 auto operator() () const
1359 {
1360 return (*this) (SelectWhole {}, ConstTrueTree_v);
1361 }
1362
1363 template<typename Single>
1364 auto operator() (Single&& single) const
1365 {
1366 if constexpr (IsExprTree<std::decay_t<Single>> {})
1367 return (*this) (SelectWhole {}, std::forward<Single> (single));
1368 else
1369 return (*this) (std::forward<Single> (single), ConstTrueTree_v);
1370 }
1371
1372 template<
1373 typename Selector,
1374 ExprType Type, typename L, typename R,
1375 typename Order = OrderNone,
1376 typename Group = GroupNone,
1377 typename Limit = LimitNone,
1378 typename Offset = OffsetNone
1379 >
1380 auto operator() (Selector,
1381 const ExprTree<Type, L, R>& tree,
1382 Order order = OrderNone {},
1383 Group group = GroupNone {},
1384 Limit limit = LimitNone {},
1385 Offset offset = OffsetNone {}) const
1386 {
1387 using TreeType_t = ExprTree<Type, L, R>;
1388
1389 constexpr auto where = ExprTreeToSql<"", T, TreeType_t> ();
1390 constexpr auto wherePrefix = [where]
1391 {
1392 if constexpr (where.IsEmpty ())
1393 return " "_ct;
1394 else
1395 return " WHERE "_ct;
1396 } ();
1397 constexpr auto from = BuildFromClause<TreeType_t> ();
1398 const auto binder = [&] (QSqlQuery& query)
1399 {
1400 BindExprTree<"", T> (tree, query);
1401 BindLimitOffset (query, limit, offset);
1402 };
1403 using HS = HandleSelector<T, Selector>;
1404
1405 constexpr auto query = "SELECT " + HS::Fields +
1406 " FROM " + from +
1407 wherePrefix + where +
1408 HandleOrder (std::forward<Order> (order)) +
1409 HandleGroup (std::forward<Group> (group)) +
1411 auto selectResult = Select<HS> (ToString<query> (),
1412 binder);
1413 return HandleResultBehaviour<HS::ResultBehaviour_v> (std::move (selectResult));
1414 }
1415 private:
1416 template<typename HS, typename Binder>
1417 auto Select (const QString& queryStr,
1418 Binder&& binder) const
1419 {
1420 auto query = RunQuery (queryStr, binder);
1421
1422 if constexpr (SelectBehaviour == SelectBehaviour::Some)
1423 {
1424 QList<decltype (HS::Initializer (query, 0))> result;
1425 while (query.next ())
1426 result << HS::Initializer (query, 0);
1427 return result;
1428 }
1429 else
1430 {
1431 using RetType_t = std::optional<decltype (HS::Initializer (query, 0))>;
1432 return query.next () ?
1433 RetType_t { HS::Initializer (query, 0) } :
1434 RetType_t {};
1435 }
1436 }
1437
1438 template<typename Tree>
1439 consteval static auto BuildFromClause () noexcept
1440 {
1441 if constexpr (Tree::template HasAdditionalTables<T> ())
1442 return T::ClassName + ", " + JoinTup (Nub<Tree::template AdditionalTables<T>> (), ", ");
1443 else
1444 return T::ClassName;
1445 }
1446
1447 constexpr static auto HandleOrder (OrderNone) noexcept
1448 {
1449 return ""_ct;
1450 }
1451
1452 template<auto... Ptrs>
1453 constexpr static auto HandleSuborder (sph::asc<Ptrs...>) noexcept
1454 {
1455 return std::tuple { (GetQualifiedFieldNamePtr<Ptrs> () + " ASC")... };
1456 }
1457
1458 template<auto... Ptrs>
1459 constexpr static auto HandleSuborder (sph::desc<Ptrs...>) noexcept
1460 {
1461 return std::tuple { (GetQualifiedFieldNamePtr<Ptrs> () + " DESC")... };
1462 }
1463
1464 template<typename... Suborders>
1465 constexpr static auto HandleOrder (OrderBy<Suborders...>) noexcept
1466 {
1467 return " ORDER BY " + JoinTup (std::tuple_cat (HandleSuborder (Suborders {})...), ", ");
1468 }
1469
1470 constexpr static auto HandleGroup (GroupNone) noexcept
1471 {
1472 return ""_ct;
1473 }
1474
1475 template<auto... Ptrs>
1476 constexpr static auto HandleGroup (GroupBy<Ptrs...>) noexcept
1477 {
1478 return " GROUP BY " + Join (", ", GetQualifiedFieldNamePtr<Ptrs> ()...);
1479 }
1480 };
1481
1482 template<typename T>
1484 {
1485 const QSqlDatabase DB_;
1486 public:
1487 DeleteByFieldsWrapper (const QSqlDatabase& db) noexcept
1488 : DB_ { db }
1489 {
1490 }
1491
1492 template<ExprType Type, typename L, typename R>
1493 void operator() (const ExprTree<Type, L, R>& tree) const noexcept
1494 {
1495 constexpr auto where = ExprTreeToSql<"", T, ExprTree<Type, L, R>> ();
1496
1497 constexpr auto selectAll = "DELETE FROM " + T::ClassName + " WHERE " + where;
1498
1499 QSqlQuery query { DB_ };
1500 query.prepare (ToString<selectAll> ());
1501 BindExprTree<"", T> (tree, query);
1502 query.exec ();
1503 }
1504 };
1505
1506 template<typename T>
1508 {
1509 const QSqlDatabase DB_;
1510
1511 // TODO this needn't be present of T doesn't have a PKey
1512 QSqlQuery UpdateByPKey_ { DB_ };
1513 public:
1514 AdaptUpdate (const QSqlDatabase& db) noexcept
1515 : DB_ { db }
1516 {
1517 if constexpr (HasPKey<T>)
1518 {
1519 constexpr auto pkeyIdx = PKeyIndex_v<T>;
1520 constexpr auto statements = ZipWith (FieldNames<T>, " = ", BoundFieldNames<T>);
1521 constexpr auto update = "UPDATE " + T::ClassName +
1522 " SET " + JoinTup (statements, ", ") +
1523 " WHERE " + std::get<pkeyIdx> (statements);
1524 UpdateByPKey_.prepare (ToString<update> ());
1525 }
1526 }
1527
1528 void operator() (const T& seq) requires HasPKey<T>
1529 {
1530 DoInsert (seq, UpdateByPKey_, true);
1531 }
1532
1533 template<typename SL, typename SR, ExprType WType, typename WL, typename WR>
1535 {
1536 static_assert (!ExprTree<WType, WL, WR>::template HasAdditionalTables<T> (),
1537 "joins in update statements are not supported by SQL");
1538
1539 constexpr auto setClause = ExprTreeToSql<"set_", T, AssignList<SL, SR>> ();
1540 constexpr auto whereClause = ExprTreeToSql<"where_", T, ExprTree<WType, WL, WR>> ();
1541
1542 constexpr auto update = "UPDATE " + T::ClassName +
1543 " SET " + setClause +
1544 " WHERE " + whereClause;
1545
1546 QSqlQuery query { DB_ };
1547 query.prepare (ToString<update> ());
1548 BindExprTree<"set_", T> (set, query);
1549 BindExprTree<"where_", T> (where, query);
1550 if (!query.exec ())
1551 {
1552 qCritical () << "update query execution failed";
1553 DBLock::DumpError (query);
1554 throw QueryException ("update query execution failed", std::make_shared<QSqlQuery> (query));
1555 }
1556
1557 return query.numRowsAffected ();
1558 }
1559 };
1560
1561 template<typename T, size_t... Fields>
1563 {
1564 return "UNIQUE (" + Join (", ", std::get<Fields> (FieldNames<T>)...) + ")";
1565 };
1566
1567 template<typename T, size_t... Fields>
1569 {
1570 return "PRIMARY KEY (" + Join (", ", std::get<Fields> (FieldNames<T>)...) + ")";
1571 };
1572
1573 template<typename T>
1574 constexpr auto GetConstraintsStrings () noexcept
1575 {
1576 if constexpr (requires { typename T::Constraints; })
1577 {
1578 return []<typename... Args> (Constraints<Args...>)
1579 {
1580 return std::tuple { ExtractConstraintFields<T> (Args {})... };
1581 } (typename T::Constraints {});
1582 }
1583 else
1584 return std::tuple<> {};
1585 }
1586
1587 template<typename ImplFactory, typename T, size_t... Indices>
1588 constexpr auto GetTypes (std::index_sequence<Indices...>) noexcept
1589 {
1590 return std::tuple { Type2Name<ImplFactory, ValueAtC_t<T, Indices>> {} ()... };
1591 }
1592
1593 template<auto Name, typename ImplFactory, typename T>
1594 constexpr auto AdaptCreateTableNamed () noexcept
1595 {
1596 constexpr auto types = GetTypes<ImplFactory, T> (SeqIndices<T>);
1597
1598 constexpr auto constraints = GetConstraintsStrings<T> ();
1599 constexpr auto constraintsStr = [&]
1600 {
1601 if constexpr (!std::tuple_size_v<decltype (constraints)>)
1602 return ""_ct;
1603 else
1604 return ", " + JoinTup (constraints, ", ");
1605 } ();
1606
1607 constexpr auto statements = ZipWith (FieldNames<T>, " ", types);
1608 return "CREATE TABLE " +
1609 Name +
1610 " (" +
1611 JoinTup (statements, ", ") +
1612 constraintsStr +
1613 ");";
1614 }
1615
1616 template<typename ImplFactory, typename T>
1617 constexpr auto AdaptCreateTable () noexcept
1618 {
1620 }
1621 }
1622
1623 template<typename T>
1636
1637 template<typename T, typename ImplFactory = detail::SQLite::ImplFactory>
1638 ObjectInfo<T> Adapt (const QSqlDatabase& db)
1639 {
1640 if (!db.tables ().contains (ToString<T::ClassName> (), Qt::CaseInsensitive))
1641 {
1642 constexpr auto query = detail::AdaptCreateTable<ImplFactory, T> ();
1644 }
1645
1646 return
1647 {
1648 { db },
1649 { db },
1650 { db },
1651
1652 { db },
1653 { db },
1654 { db },
1655 };
1656 }
1657
1658 template<typename T>
1659 using ObjectInfo_ptr = std::unique_ptr<ObjectInfo<T>>;
1660
1661 template<typename T, typename ImplFactory = SQLiteImplFactory>
1662 ObjectInfo_ptr<T> AdaptPtr (const QSqlDatabase& db)
1663 {
1664 return std::make_unique<ObjectInfo<T>> (Adapt<T, ImplFactory> (db));
1665 }
1666
1667 template<typename ImplFactory, typename... Ts>
1668 void AdaptPtrs (const QSqlDatabase& db, ObjectInfo_ptr<Ts>&... objects)
1669 {
1670 ((objects = AdaptPtr<Ts, ImplFactory> (db)), ...);
1671 }
1672}
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 &q)
Definition oral.h:109
QueryException(const std::string &str, const QSqlQuery_ptr &q)
Definition oral.h:104
const QSqlQuery & GetQuery() const
Definition oral.h:117
~QueryException() noexcept=default
auto operator()(Seq &t, Action action={}) const
Definition oral.h:412
AdaptInsert(const QSqlDatabase &db) noexcept
Definition oral.h:406
AdaptUpdate(const QSqlDatabase &db) noexcept
Definition oral.h:1514
void operator()(const T &seq)
Definition oral.h:1528
constexpr AssignList(const L &l, const R &r) noexcept
Definition oral.h:612
void BindValues(QSqlQuery &query) const noexcept
Definition oral.h:628
static constexpr auto ToSql() noexcept
Definition oral.h:619
constexpr auto operator,(const AssignList< OL, OR > &tail) noexcept
Definition oral.h:635
DeleteByFieldsWrapper(const QSqlDatabase &db) noexcept
Definition oral.h:1487
void operator()(const ExprTree< Type, L, R > &tree) const noexcept
Definition oral.h:1493
void BindValues(QSqlQuery &query) const noexcept
Definition oral.h:702
static constexpr bool HasAdditionalTables() noexcept
Definition oral.h:715
constexpr ExprTree(const L &l, const R &r) noexcept
Definition oral.h:647
static constexpr bool HasAdditionalTables() noexcept
Definition oral.h:676
void BindValues(QSqlQuery &query) const noexcept
Definition oral.h:663
static constexpr auto ToSql() noexcept
Definition oral.h:654
static constexpr auto AdditionalTables() noexcept
Definition oral.h:670
auto RunQuery(const QString &queryStr, auto &&binder) const
Definition oral.h:1126
SelectWrapperCommon(const QSqlDatabase &db) noexcept
Definition oral.h:1121
SelectWrapperCommon(const QSqlDatabase &db) noexcept
Definition oral.h:1121
auto Build() const noexcept
Definition oral.h:1344
QSqlQuery RunTextQuery(const QSqlDatabase &db, const QString &text)
Runs the given query text on the given db.
Definition util.cpp:18
constexpr auto GetTypes(std::index_sequence< Indices... >) noexcept
Definition oral.h:1588
constexpr auto AdaptCreateTableNamed() noexcept
Definition oral.h:1594
auto operator>(const L &left, const R &right) noexcept
Definition oral.h:831
auto MakeIndexedQueryHandler(const QSqlQuery &q, int startIdx=0) noexcept
Definition oral.h:999
consteval int PKeyIndex()
Definition oral.h:366
constexpr auto GetFieldNamePtr() noexcept
Definition oral.h:196
constexpr auto GetQualifiedFieldNamePtr() noexcept
Definition oral.h:203
constexpr auto ExprTreeToSql() noexcept
Definition oral.h:890
constexpr auto AsLeafData(const T &node) noexcept
Definition oral.h:722
constexpr auto LimitOffsetToString() noexcept
Definition oral.h:1145
auto operator||(const L &left, const R &right) noexcept
Definition oral.h:884
T InitializeFromQuery(const QSqlQuery &q, std::index_sequence< Indices... >, int startIdx) noexcept
Definition oral.h:505
void BindLimitOffset(QSqlQuery &query, L limit, O offset) noexcept
Definition oral.h:1164
auto Combine(std::tuple< LArgs... > &&left, std::tuple< RArgs... > &&right) noexcept
Definition oral.h:1068
constexpr auto GetConstraintsStrings() noexcept
Definition oral.h:1574
std::enable_if_t< EitherIsExprTree< L, R >()> EnableRelOp_t
Definition oral.h:822
consteval int PKeyIndexUnsafe()
Definition oral.h:354
decltype(auto) HandleResultBehaviour(ResList &&list) noexcept
Definition oral.h:1098
constexpr CountAll * CountAllPtr
Definition oral.h:913
constexpr auto ConstTrueTree_v
Definition oral.h:801
auto DoInsert(const Seq &seq, QSqlQuery &insertQuery, bool bindPrimaryKey)
Definition oral.h:338
constexpr decltype(auto) Get(const Seq &seq)
Definition oral.h:138
QVariant ToVariantF(const T &t) noexcept
Definition oral.h:325
constexpr bool IsRelational(ExprType type) noexcept
Definition oral.h:562
constexpr auto AdaptCreateTable() noexcept
Definition oral.h:1617
auto MemberFromVariant(const QVariant &var) noexcept
Definition oral.h:993
constexpr auto FieldNames
Definition oral.h:171
constexpr auto ExtractConstraintFields(UniqueSubset< Fields... >)
Definition oral.h:1562
consteval auto GetFieldName()
Definition oral.h:161
constexpr auto TypeToSql() noexcept
Definition oral.h:538
constexpr decltype(auto) GetReplaceTupleElem(Tuple &&tuple, NewType &&arg) noexcept
Definition oral.h:1024
SelectorUnion< L, R > operator+(L, R) noexcept
Definition oral.h:942
constexpr auto BoundFieldNames
Definition oral.h:177
constexpr auto ReplaceTupleElem(std::tuple< TupleArgs... > &&tuple, NewType &&arg) noexcept
Definition oral.h:1042
auto operator!=(const L &left, const R &right) noexcept
Definition oral.h:843
constexpr auto CombineBehaviour(ResultBehaviour l, ResultBehaviour r) noexcept
Definition oral.h:1247
constexpr bool EitherIsExprTree() noexcept
Definition oral.h:812
HandleSelectorResult(QString, F, R) -> HandleSelectorResult< F, R >
auto MakeExprTree(const L &left, const R &right) noexcept
Definition oral.h:804
void BindAtIndex(const Seq &seq, QSqlQuery &query, bool bindPrimaryKey)
Definition oral.h:331
constexpr auto QualifiedFieldNames
Definition oral.h:183
std::decay_t< decltype(Get< Idx >(std::declval< Seq >()))> ValueAtC_t
Definition oral.h:316
constexpr auto ReplaceTupleElemImpl(Tuple &&tuple, NewType &&arg, std::index_sequence< TupIdxs... >) noexcept
Definition oral.h:1033
constexpr auto ExtractConflictingFields(InsertAction::Replace::PKeyType)
Definition oral.h:389
constexpr bool Typecheck()
Definition oral.h:585
typename std::conditional_t< IsIndirect< T > {}, T, WrapDirect< T > >::value_type UnwrapIndirect_t
Definition oral.h:580
auto operator==(const L &left, const R &right) noexcept
Definition oral.h:837
void BindExprTree(const Tree &tree, QSqlQuery &query)
Definition oral.h:896
auto operator<(const L &left, const R &right) noexcept
Definition oral.h:825
constexpr size_t FieldIndex() noexcept
Definition oral.h:189
constexpr int PKeyIndex_v
Definition oral.h:374
consteval auto MorphFieldName()
Definition oral.h:150
constexpr auto SeqIndices
Definition oral.h:168
constexpr auto HasAutogenPKey() noexcept
Definition oral.h:380
auto operator|(const L &left, InfixBinary< Op >) noexcept
Definition oral.h:866
auto operator&&(const L &left, const R &right) noexcept
Definition oral.h:878
constexpr detail::InfixBinary< detail::ExprType::Like > like
Definition oral.h:854
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition oral.h:951
constexpr detail::SelectWhole all
Definition oral.h:956
constexpr detail::AggregateType< detail::AggregateFunction::Count, Ptr > count
Definition oral.h:965
constexpr detail::AggregateType< detail::AggregateFunction::Min, Ptr > min
Definition oral.h:968
constexpr detail::MemberPtrs< Ptrs... > fields
Definition oral.h:954
constexpr detail::AggregateType< detail::AggregateFunction::Max, Ptr > max
Definition oral.h:971
void AdaptPtrs(const QSqlDatabase &db, ObjectInfo_ptr< Ts > &... objects)
Definition oral.h:1668
Typelist< Args... > Constraints
Definition oraltypes.h:139
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
Definition oral.h:1662
constexpr detail::OrderBy< Orders... > OrderBy
Definition oral.h:975
Typelist< Args... > Indices
Definition oraltypes.h:145
constexpr size_t SeqSize
Definition oral.h:124
constexpr detail::GroupBy< Ptrs... > GroupBy
Definition oral.h:978
ObjectInfo< T > Adapt(const QSqlDatabase &db)
Definition oral.h:1638
std::unique_ptr< ObjectInfo< T > > ObjectInfo_ptr
Definition oral.h:1659
std::shared_ptr< QSqlQuery > QSqlQuery_ptr
Definition oral.h:98
typename AsTypelist< T >::Result_t AsTypelist_t
Definition typelist.h:140
std::tuple_element_t< 0, detail::CallTypeGetter_t< F > > RetType_t
Definition typegetter.h:43
constexpr auto ZipWith(Tup1 &&tup1, auto &&sep, Tup2 &&tup2) noexcept
constexpr auto JoinTup(auto &&stringsTuple, auto &&sep) noexcept
constexpr bool HasType(List< Args... >)
Definition typelist.h:95
QString ToString()
Definition ctstring.h:130
MemberTypeType_t< decltype(Ptr)> MemberPtrType_t
Definition typegetter.h:70
constexpr auto Nub()
constexpr auto Join(auto &&) noexcept
MemberTypeStruct_t< decltype(Ptr)> MemberPtrStruct_t
Definition typegetter.h:73
STL namespace.
T operator()(const QVariant &var) const noexcept
Definition oral.h:296
detail::AdaptUpdate< T > Update
Definition oral.h:1627
detail::SelectWrapper< T, detail::SelectBehaviour::Some > Select
Definition oral.h:1630
detail::AdaptDelete< T > Delete
Definition oral.h:1628
detail::AdaptInsert< T > Insert
Definition oral.h:1626
detail::DeleteByFieldsWrapper< T > DeleteBy
Definition oral.h:1632
detail::SelectWrapper< T, detail::SelectBehaviour::One > SelectOne
Definition oral.h:1631
QVariant operator()(const T &t) const noexcept
Definition oral.h:276
constexpr auto operator()() const noexcept
Definition oral.h:219
void operator()(const Seq &seq)
Definition oral.h:495
AdaptDelete(const QSqlDatabase &db) noexcept
Definition oral.h:483
static constexpr int Value
Definition oral.h:1052
static constexpr auto ResultBehaviour_v
Definition oral.h:1175
static constexpr auto ResultBehaviour_v
Definition oral.h:1177
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition oral.h:1235
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition oral.h:1202
static auto Initializer(const QSqlQuery &q, int startIdx)
Definition oral.h:1184
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition oral.h:1264