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