LeechCraft 0.6.70-17609-g3dde4097dd
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>
913 {
914 explicit SelectDistinct (T) noexcept {}
915 };
916
917 template<typename T>
918 struct IsSelector : std::false_type {};
919
920 template<>
921 struct IsSelector<SelectWhole> : std::true_type {};
922
923 template<AggregateFunction Fun, auto Ptr>
924 struct IsSelector<AggregateType<Fun, Ptr>> : std::true_type {};
925
926 template<auto... Ptrs>
927 struct IsSelector<MemberPtrs<Ptrs...>> : std::true_type {};
928
929 template<typename L, typename R>
930 struct IsSelector<SelectorUnion<L, R>> : std::true_type {};
931
932 template<typename T>
933 struct IsSelector<SelectDistinct<T>> : std::true_type {};
934
935 template<typename L, typename R, typename = std::enable_if_t<IsSelector<L> {} && IsSelector<R> {}>>
936 SelectorUnion<L, R> operator+ (L, R) noexcept
937 {
938 return {};
939 }
940 }
941
942 namespace sph
943 {
944 template<auto Ptr>
946
947 template<auto... Ptrs>
948 constexpr detail::MemberPtrs<Ptrs...> fields {};
949
951
952 template<typename T>
954
955 template<auto... Ptrs>
956 struct asc {};
957
958 template<auto... Ptrs>
959 struct desc {};
960
961 template<auto Ptr = detail::CountAllPtr>
963
964 template<auto Ptr>
966
967 template<auto Ptr>
969 };
970
971 template<typename... Orders>
972 constexpr detail::OrderBy<Orders...> OrderBy {};
973
974 template<auto... Ptrs>
975 constexpr detail::GroupBy<Ptrs...> GroupBy {};
976
977 struct Limit
978 {
979 uint64_t Count;
980 };
981
982 struct Offset
983 {
984 uint64_t Count;
985 };
986
987 namespace detail
988 {
989 template<auto Ptr>
990 auto MemberFromVariant (const QVariant& var) noexcept
991 {
993 }
994
995 template<auto Ptr>
996 auto MakeIndexedQueryHandler (const QSqlQuery& q, int startIdx = 0) noexcept
997 {
998 return MemberFromVariant<Ptr> (q.value (startIdx));
999 }
1000
1001 template<auto... Ptrs>
1002 auto MakeIndexedQueryHandler (MemberPtrs<Ptrs...>, const QSqlQuery& q, int startIdx) noexcept
1003 {
1004 if constexpr (sizeof... (Ptrs) == 1)
1005 return MakeIndexedQueryHandler<Ptrs...> (q, startIdx);
1006 else
1007 return [&]<size_t... Ix> (std::index_sequence<Ix...>)
1008 {
1009 return std::tuple { MemberFromVariant<Ptrs> (q.value (startIdx + Ix))... };
1010 } (std::make_index_sequence<sizeof... (Ptrs)> {});
1011 }
1012
1013 enum class SelectBehaviour { Some, One };
1014
1015 struct OrderNone {};
1016 struct GroupNone {};
1017 struct LimitNone {};
1018 struct OffsetNone {};
1019
1020 template<size_t RepIdx, size_t TupIdx, typename Tuple, typename NewType>
1021 constexpr decltype (auto) GetReplaceTupleElem (Tuple&& tuple, NewType&& arg) noexcept
1022 {
1023 if constexpr (RepIdx == TupIdx)
1024 return std::forward<NewType> (arg);
1025 else
1026 return std::get<TupIdx> (tuple);
1027 }
1028
1029 template<size_t RepIdx, typename NewType, typename Tuple, size_t... TupIdxs>
1030 constexpr auto ReplaceTupleElemImpl (Tuple&& tuple, NewType&& arg, std::index_sequence<TupIdxs...>) noexcept
1031 {
1032 return std::tuple
1033 {
1034 GetReplaceTupleElem<RepIdx, TupIdxs> (std::forward<Tuple> (tuple), std::forward<NewType> (arg))...
1035 };
1036 }
1037
1038 template<size_t RepIdx, typename NewType, typename... TupleArgs>
1039 constexpr auto ReplaceTupleElem (std::tuple<TupleArgs...>&& tuple, NewType&& arg) noexcept
1040 {
1041 return ReplaceTupleElemImpl<RepIdx> (std::move (tuple),
1042 std::forward<NewType> (arg),
1043 std::index_sequence_for<TupleArgs...> {});
1044 }
1045
1046 template<typename Seq, typename T>
1048 {
1049 constexpr static int Value = 1;
1050 };
1051
1052 template<typename Seq, typename... Args>
1053 struct DetectShift<Seq, std::tuple<Args...>>
1054 {
1055 constexpr static int Value = (DetectShift<Seq, Args>::Value + ...);
1056 };
1057
1058 template<typename Seq>
1059 struct DetectShift<Seq, Seq>
1060 {
1061 constexpr static int Value = SeqSize<Seq>;
1062 };
1063
1064 template<typename... LArgs, typename... RArgs>
1065 auto Combine (std::tuple<LArgs...>&& left, std::tuple<RArgs...>&& right) noexcept
1066 {
1067 return std::tuple_cat (std::move (left), std::move (right));
1068 }
1069
1070 template<typename... LArgs, typename R>
1071 auto Combine (std::tuple<LArgs...>&& left, const R& right) noexcept
1072 {
1073 return std::tuple_cat (std::move (left), std::tuple { right });
1074 }
1075
1076 template<typename L, typename... RArgs>
1077 auto Combine (const L& left, std::tuple<RArgs...>&& right) noexcept
1078 {
1079 return std::tuple_cat (std::tuple { left }, std::move (right));
1080 }
1081
1082 template<typename L, typename R>
1083 auto Combine (const L& left, const R& right) noexcept
1084 {
1085 return std::tuple { left, right };
1086 }
1087
1089 {
1092 };
1093
1094 template<ResultBehaviour ResultBehaviour, typename ResList>
1095 decltype (auto) HandleResultBehaviour (ResList&& list) noexcept
1096 {
1097 if constexpr (ResultBehaviour == ResultBehaviour::All)
1098 return std::forward<ResList> (list);
1099 else if constexpr (ResultBehaviour == ResultBehaviour::First)
1100 return list.value (0);
1101 }
1102
1103 template<typename F, typename R>
1110
1111 template<typename F, typename R>
1113
1115 {
1116 const QSqlDatabase DB_;
1117 public:
1118 SelectWrapperCommon (const QSqlDatabase& db) noexcept
1119 : DB_ { db }
1120 {
1121 }
1122 protected:
1123 auto RunQuery (const QString& queryStr,
1124 auto&& binder) const
1125 {
1126 QSqlQuery query { DB_ };
1127 query.prepare (queryStr);
1128 binder (query);
1129
1130 if (!query.exec ())
1131 {
1132 qCritical () << "select query execution failed";
1133 DBLock::DumpError (query);
1134 throw QueryException ("fetch query execution failed");
1135 }
1136
1137 return query;
1138 }
1139 };
1140
1141 template<typename L, typename O>
1142 constexpr auto LimitOffsetToString () noexcept
1143 {
1144 if constexpr (std::is_same_v<L, LimitNone>)
1145 {
1146 static_assert (std::is_same_v<O, OffsetNone>, "LIMIT-less queries currently cannot have OFFSET");
1147 return ""_ct;
1148 }
1149 else
1150 return " LIMIT :limit "_ct +
1151 [] () constexpr
1152 {
1153 if constexpr (std::is_same_v<O, OffsetNone>)
1154 return ""_ct;
1155 else
1156 return " OFFSET :offset"_ct;
1157 } ();
1158 }
1159
1160 template<typename L, typename O>
1161 void BindLimitOffset (QSqlQuery& query, L limit, O offset) noexcept
1162 {
1163 if constexpr (!std::is_same_v<std::decay_t<L>, LimitNone>)
1164 query.bindValue (":limit", qulonglong { limit.Count });
1165 if constexpr (!std::is_same_v<std::decay_t<O>, OffsetNone>)
1166 query.bindValue (":offset", qulonglong { offset.Count });
1167 }
1168
1169 template<typename T, typename Selector>
1171
1172 struct HSBaseAll { constexpr static auto ResultBehaviour_v = ResultBehaviour::All; };
1173
1174 struct HSBaseFirst { constexpr static auto ResultBehaviour_v = ResultBehaviour::First; };
1175
1176 template<typename T>
1178 {
1179 constexpr static auto Fields = JoinTup (QualifiedFieldNames<T>, ", "_ct);
1180
1181 static auto Initializer (const QSqlQuery& q, int startIdx)
1182 {
1183 return InitializeFromQuery<T> (q, SeqIndices<T>, startIdx);
1184 }
1185 };
1186
1187 template<typename T, typename U>
1189 {
1190 constexpr static auto Fields = "DISTINCT "_ct + HandleSelector<T, U>::Fields;
1191 };
1192
1193 template<typename T, auto... Ptrs>
1195 {
1196 private:
1197 template<size_t... Ixs>
1198 constexpr static auto SelectFields ()
1199 {
1200 return std::tuple { std::get<Ixs> (QualifiedFieldNames<T>)... };
1201 }
1202 public:
1203 constexpr static auto Fields = JoinTup (SelectFields<FieldIndex<Ptrs> ()...> (), ", ");
1204
1205 static auto Initializer (const QSqlQuery& q, int startIdx) noexcept
1206 {
1207 return MakeIndexedQueryHandler (MemberPtrs<Ptrs...> {}, q, startIdx);
1208 }
1209 };
1210
1211 template<typename T>
1213 {
1214 constexpr static auto Fields = "count(1)"_ct;
1215
1216 static auto Initializer (const QSqlQuery& q, int startIdx)
1217 {
1218 return q.value (startIdx).toLongLong ();
1219 }
1220 };
1221
1222 template<typename T, auto Ptr>
1224 {
1225 constexpr static auto Fields = "count(" + GetQualifiedFieldNamePtr<Ptr> () + ")";
1226
1227 static auto Initializer (const QSqlQuery& q, int startIdx)
1228 {
1229 return q.value (startIdx).toLongLong ();
1230 }
1231 };
1232
1233 template<CtString Aggregate, typename T, auto Ptr>
1235 {
1236 constexpr static auto Fields = Aggregate + "(" + GetQualifiedFieldNamePtr<Ptr> () + ")";
1237
1238 static auto Initializer (const QSqlQuery& q, int startIdx) noexcept
1239 {
1240 return MakeIndexedQueryHandler<Ptr> (q, startIdx);
1241 }
1242 };
1243
1244 template<typename T, auto Ptr>
1245 struct HandleSelector<T, AggregateType<AggregateFunction::Min, Ptr>> : HandleAggSelector<"min"_ct, T, Ptr> {};
1246
1247 template<typename T, auto Ptr>
1248 struct HandleSelector<T, AggregateType<AggregateFunction::Max, Ptr>> : HandleAggSelector<"max"_ct, T, Ptr> {};
1249
1250 constexpr auto CombineBehaviour (ResultBehaviour l, ResultBehaviour r) noexcept
1251 {
1254 return ResultBehaviour::All;
1255 }
1256
1257 template<typename T, typename L, typename R>
1259 {
1262
1263 constexpr static auto ResultBehaviour_v = CombineBehaviour (HL::ResultBehaviour_v, HR::ResultBehaviour_v);
1264
1265 constexpr static auto Fields = HL::Fields + ", " + HR::Fields;
1266
1267 static auto Initializer (const QSqlQuery& q, int startIdx) noexcept
1268 {
1269 constexpr auto shift = DetectShift<T, decltype (HL::Initializer (q, 0))>::Value;
1270 return Combine (HL::Initializer (q, startIdx), HR::Initializer (q, startIdx + shift));
1271 }
1272 };
1273
1274 template<typename T, SelectBehaviour SelectBehaviour>
1276 {
1277 template<typename ParamsTuple>
1278 struct Builder
1279 {
1280 const SelectWrapper& W_;
1281 ParamsTuple Params_;
1282
1283 template<typename NewTuple>
1284 constexpr auto RepTuple (NewTuple&& tuple) noexcept
1285 {
1286 return Builder<NewTuple> { W_, tuple };
1287 }
1288
1289 template<typename U>
1290 constexpr auto Select (U&& selector) && noexcept
1291 {
1292 return RepTuple (ReplaceTupleElem<0> (std::move (Params_), std::forward<U> (selector)));
1293 }
1294
1295 template<typename U>
1296 constexpr auto Where (U&& tree) && noexcept
1297 {
1298 return RepTuple (ReplaceTupleElem<1> (std::move (Params_), std::forward<U> (tree)));
1299 }
1300
1301 template<typename U>
1302 constexpr auto Order (U&& order) && noexcept
1303 {
1304 return RepTuple (ReplaceTupleElem<2> (std::move (Params_), std::forward<U> (order)));
1305 }
1306
1307 template<typename U>
1308 constexpr auto Group (U&& group) && noexcept
1309 {
1310 return RepTuple (ReplaceTupleElem<3> (std::move (Params_), std::forward<U> (group)));
1311 }
1312
1313 constexpr auto Limit (Limit limit) && noexcept
1314 {
1315 return RepTuple (ReplaceTupleElem<4> (std::move (Params_), limit));
1316 }
1317
1318 constexpr auto Limit (uint64_t limit) && noexcept
1319 {
1320 return std::move (*this).Limit (oral::Limit { limit });
1321 }
1322
1323 constexpr auto Offset (Offset offset) && noexcept
1324 {
1325 return RepTuple (ReplaceTupleElem<5> (std::move (Params_), offset));
1326 }
1327
1328 constexpr auto Offset (uint64_t offset) && noexcept
1329 {
1330 return std::move (*this).Offset (oral::Offset { offset });
1331 }
1332
1333 auto operator() () &&
1334 {
1335 return std::apply (W_, Params_);
1336 }
1337
1338 template<auto... Ptrs>
1339 constexpr auto Group () && noexcept
1340 {
1341 return std::move (*this).Group (GroupBy<Ptrs...> {});
1342 }
1343 };
1344 public:
1346
1347 auto Build () const noexcept
1348 {
1349 std::tuple defParams
1350 {
1351 SelectWhole {},
1353 OrderNone {},
1354 GroupNone {},
1355 LimitNone {},
1356 OffsetNone {}
1357 };
1358 return Builder<decltype (defParams)> { *this, defParams };
1359 }
1360
1361 auto operator() () const
1362 {
1363 return (*this) (SelectWhole {}, ConstTrueTree_v);
1364 }
1365
1366 template<typename Single>
1367 auto operator() (Single&& single) const
1368 {
1369 if constexpr (IsExprTree<std::decay_t<Single>> {})
1370 return (*this) (SelectWhole {}, std::forward<Single> (single));
1371 else
1372 return (*this) (std::forward<Single> (single), ConstTrueTree_v);
1373 }
1374
1375 template<
1376 typename Selector,
1377 ExprType Type, typename L, typename R,
1378 typename Order = OrderNone,
1379 typename Group = GroupNone,
1380 typename Limit = LimitNone,
1381 typename Offset = OffsetNone
1382 >
1383 auto operator() (Selector,
1384 const ExprTree<Type, L, R>& tree,
1385 Order order = OrderNone {},
1386 Group group = GroupNone {},
1387 Limit limit = LimitNone {},
1388 Offset offset = OffsetNone {}) const
1389 {
1390 using TreeType_t = ExprTree<Type, L, R>;
1391
1392 constexpr auto where = ExprTreeToSql<"", T, TreeType_t> ();
1393 constexpr auto wherePrefix = [where]
1394 {
1395 if constexpr (where.IsEmpty ())
1396 return " "_ct;
1397 else
1398 return " WHERE "_ct;
1399 } ();
1400 constexpr auto from = BuildFromClause<TreeType_t> ();
1401 const auto binder = [&] (QSqlQuery& query)
1402 {
1403 BindExprTree<"", T> (tree, query);
1404 BindLimitOffset (query, limit, offset);
1405 };
1406 using HS = HandleSelector<T, Selector>;
1407
1408 constexpr auto query = "SELECT " + HS::Fields +
1409 " FROM " + from +
1410 wherePrefix + where +
1411 HandleOrder (std::forward<Order> (order)) +
1412 HandleGroup (std::forward<Group> (group)) +
1413 LimitOffsetToString<Limit, Offset> ();
1414 auto selectResult = Select<HS> (ToString<query> (),
1415 binder);
1416 return HandleResultBehaviour<HS::ResultBehaviour_v> (std::move (selectResult));
1417 }
1418 private:
1419 template<typename HS, typename Binder>
1420 auto Select (const QString& queryStr,
1421 Binder&& binder) const
1422 {
1423 auto query = RunQuery (queryStr, binder);
1424
1425 if constexpr (SelectBehaviour == SelectBehaviour::Some)
1426 {
1427 QList<decltype (HS::Initializer (query, 0))> result;
1428 while (query.next ())
1429 result << HS::Initializer (query, 0);
1430 return result;
1431 }
1432 else
1433 {
1434 using RetType_t = std::optional<decltype (HS::Initializer (query, 0))>;
1435 return query.next () ?
1436 RetType_t { HS::Initializer (query, 0) } :
1437 RetType_t {};
1438 }
1439 }
1440
1441 template<typename Tree>
1442 consteval static auto BuildFromClause () noexcept
1443 {
1444 if constexpr (Tree::template HasAdditionalTables<T> ())
1445 return T::ClassName + ", " + JoinTup (Nub<Tree::template AdditionalTables<T>> (), ", ");
1446 else
1447 return T::ClassName;
1448 }
1449
1450 constexpr static auto HandleOrder (OrderNone) noexcept
1451 {
1452 return ""_ct;
1453 }
1454
1455 template<auto... Ptrs>
1456 constexpr static auto HandleSuborder (sph::asc<Ptrs...>) noexcept
1457 {
1458 return std::tuple { (GetQualifiedFieldNamePtr<Ptrs> () + " ASC")... };
1459 }
1460
1461 template<auto... Ptrs>
1462 constexpr static auto HandleSuborder (sph::desc<Ptrs...>) noexcept
1463 {
1464 return std::tuple { (GetQualifiedFieldNamePtr<Ptrs> () + " DESC")... };
1465 }
1466
1467 template<typename... Suborders>
1468 constexpr static auto HandleOrder (OrderBy<Suborders...>) noexcept
1469 {
1470 return " ORDER BY " + JoinTup (std::tuple_cat (HandleSuborder (Suborders {})...), ", ");
1471 }
1472
1473 constexpr static auto HandleGroup (GroupNone) noexcept
1474 {
1475 return ""_ct;
1476 }
1477
1478 template<auto... Ptrs>
1479 constexpr static auto HandleGroup (GroupBy<Ptrs...>) noexcept
1480 {
1481 return " GROUP BY " + Join (", ", GetQualifiedFieldNamePtr<Ptrs> ()...);
1482 }
1483 };
1484
1485 template<typename T>
1487 {
1488 const QSqlDatabase DB_;
1489 public:
1490 DeleteByFieldsWrapper (const QSqlDatabase& db) noexcept
1491 : DB_ { db }
1492 {
1493 }
1494
1495 template<ExprType Type, typename L, typename R>
1496 void operator() (const ExprTree<Type, L, R>& tree) const noexcept
1497 {
1498 constexpr auto where = ExprTreeToSql<"", T, ExprTree<Type, L, R>> ();
1499
1500 constexpr auto selectAll = "DELETE FROM " + T::ClassName + " WHERE " + where;
1501
1502 QSqlQuery query { DB_ };
1503 query.prepare (ToString<selectAll> ());
1504 BindExprTree<"", T> (tree, query);
1505 query.exec ();
1506 }
1507 };
1508
1509 template<typename T>
1511 {
1512 const QSqlDatabase DB_;
1513
1514 // TODO this needn't be present of T doesn't have a PKey
1515 QSqlQuery UpdateByPKey_ { DB_ };
1516 public:
1517 AdaptUpdate (const QSqlDatabase& db) noexcept
1518 : DB_ { db }
1519 {
1520 if constexpr (HasPKey<T>)
1521 {
1522 constexpr auto pkeyIdx = PKeyIndex_v<T>;
1523 constexpr auto statements = ZipWith (FieldNames<T>, " = ", BoundFieldNames<T>);
1524 constexpr auto update = "UPDATE " + T::ClassName +
1525 " SET " + JoinTup (statements, ", ") +
1526 " WHERE " + std::get<pkeyIdx> (statements);
1527 UpdateByPKey_.prepare (ToString<update> ());
1528 }
1529 }
1530
1531 void operator() (const T& seq) requires HasPKey<T>
1532 {
1533 DoInsert (seq, UpdateByPKey_, true);
1534 }
1535
1536 template<typename SL, typename SR, ExprType WType, typename WL, typename WR>
1537 int operator() (const AssignList<SL, SR>& set, const ExprTree<WType, WL, WR>& where)
1538 {
1539 static_assert (!ExprTree<WType, WL, WR>::template HasAdditionalTables<T> (),
1540 "joins in update statements are not supported by SQL");
1541
1542 constexpr auto setClause = ExprTreeToSql<"set_", T, AssignList<SL, SR>> ();
1543 constexpr auto whereClause = ExprTreeToSql<"where_", T, ExprTree<WType, WL, WR>> ();
1544
1545 constexpr auto update = "UPDATE " + T::ClassName +
1546 " SET " + setClause +
1547 " WHERE " + whereClause;
1548
1549 QSqlQuery query { DB_ };
1550 query.prepare (ToString<update> ());
1551 BindExprTree<"set_", T> (set, query);
1552 BindExprTree<"where_", T> (where, query);
1553 if (!query.exec ())
1554 {
1555 qCritical () << "update query execution failed";
1556 DBLock::DumpError (query);
1557 throw QueryException ("update query execution failed");
1558 }
1559
1560 return query.numRowsAffected ();
1561 }
1562 };
1563
1564 template<typename T, size_t... Fields>
1566 {
1567 return "UNIQUE (" + Join (", ", std::get<Fields> (FieldNames<T>)...) + ")";
1568 };
1569
1570 template<typename T, size_t... Fields>
1572 {
1573 return "PRIMARY KEY (" + Join (", ", std::get<Fields> (FieldNames<T>)...) + ")";
1574 };
1575
1576 template<typename T>
1577 constexpr auto GetConstraintsStrings () noexcept
1578 {
1579 if constexpr (requires { typename T::Constraints; })
1580 {
1581 return []<typename... Args> (Constraints<Args...>)
1582 {
1583 return std::tuple { ExtractConstraintFields<T> (Args {})... };
1584 } (typename T::Constraints {});
1585 }
1586 else
1587 return std::tuple<> {};
1588 }
1589
1590 template<typename ImplFactory, typename T, size_t... Indices>
1591 constexpr auto GetTypes (std::index_sequence<Indices...>) noexcept
1592 {
1593 return std::tuple { Type2Name<ImplFactory, ValueAtC_t<T, Indices>> {} ()... };
1594 }
1595
1596 template<auto Name, typename ImplFactory, typename T>
1597 constexpr auto AdaptCreateTableNamed () noexcept
1598 {
1599 constexpr auto types = GetTypes<ImplFactory, T> (SeqIndices<T>);
1600
1601 constexpr auto constraints = GetConstraintsStrings<T> ();
1602 constexpr auto constraintsStr = [&]
1603 {
1604 if constexpr (!std::tuple_size_v<decltype (constraints)>)
1605 return ""_ct;
1606 else
1607 return ", " + JoinTup (constraints, ", ");
1608 } ();
1609
1610 constexpr auto statements = ZipWith (FieldNames<T>, " ", types);
1611 return "CREATE TABLE " +
1612 Name +
1613 " (" +
1614 JoinTup (statements, ", ") +
1615 constraintsStr +
1616 ");";
1617 }
1618
1619 template<typename ImplFactory, typename T>
1620 constexpr auto AdaptCreateTable () noexcept
1621 {
1623 }
1624 }
1625
1626 template<typename T>
1639
1640 template<typename T, typename ImplFactory = detail::SQLite::ImplFactory>
1641 ObjectInfo<T> Adapt (const QSqlDatabase& db)
1642 {
1643 if (!db.tables ().contains (ToString<T::ClassName> (), Qt::CaseInsensitive))
1644 {
1645 constexpr auto query = detail::AdaptCreateTable<ImplFactory, T> ();
1647 }
1648
1649 return
1650 {
1651 { db },
1652 { db },
1653 { db },
1654
1655 { db },
1656 { db },
1657 { db },
1658 };
1659 }
1660
1661 template<typename T>
1662 using ObjectInfo_ptr = std::unique_ptr<ObjectInfo<T>>;
1663
1664 template<typename T, typename ImplFactory = SQLiteImplFactory>
1665 ObjectInfo_ptr<T> AdaptPtr (const QSqlDatabase& db)
1666 {
1667 return std::make_unique<ObjectInfo<T>> (Adapt<T, ImplFactory> (db));
1668 }
1669
1670 template<typename ImplFactory, typename... Ts>
1671 void AdaptPtrs (const QSqlDatabase& db, ObjectInfo_ptr<Ts>&... objects)
1672 {
1673 ((objects = AdaptPtr<Ts, ImplFactory> (db)), ...);
1674 }
1675}
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:1517
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:1490
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:1123
SelectWrapperCommon(const QSqlDatabase &db) noexcept
Definition oral.h:1118
SelectWrapperCommon(const QSqlDatabase &db) noexcept
Definition oral.h:1118
auto Build() const noexcept
Definition oral.h:1347
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:1591
constexpr auto AdaptCreateTableNamed() noexcept
Definition oral.h:1597
auto MakeIndexedQueryHandler(const QSqlQuery &q, int startIdx=0) noexcept
Definition oral.h:996
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:1142
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:1161
auto Combine(std::tuple< LArgs... > &&left, std::tuple< RArgs... > &&right) noexcept
Definition oral.h:1065
constexpr auto GetConstraintsStrings() noexcept
Definition oral.h:1577
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:1095
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:1620
auto MemberFromVariant(const QVariant &var) noexcept
Definition oral.h:990
constexpr auto FieldNames
Definition oral.h:156
constexpr auto ExtractConstraintFields(UniqueSubset< Fields... >)
Definition oral.h:1565
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:1021
constexpr auto BoundFieldNames
Definition oral.h:162
constexpr auto ReplaceTupleElem(std::tuple< TupleArgs... > &&tuple, NewType &&arg) noexcept
Definition oral.h:1039
constexpr auto CombineBehaviour(ResultBehaviour l, ResultBehaviour r) noexcept
Definition oral.h:1250
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:1030
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:945
constexpr detail::SelectWhole all
Definition oral.h:950
constexpr detail::AggregateType< detail::AggregateFunction::Count, Ptr > count
Definition oral.h:962
constexpr detail::AggregateType< detail::AggregateFunction::Min, Ptr > min
Definition oral.h:965
detail::SelectDistinct< T > distinct
Definition oral.h:953
constexpr detail::MemberPtrs< Ptrs... > fields
Definition oral.h:948
constexpr detail::AggregateType< detail::AggregateFunction::Max, Ptr > max
Definition oral.h:968
void AdaptPtrs(const QSqlDatabase &db, ObjectInfo_ptr< Ts > &... objects)
Definition oral.h:1671
Typelist< Args... > Constraints
Definition oraltypes.h:139
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
Definition oral.h:1665
constexpr detail::OrderBy< Orders... > OrderBy
Definition oral.h:972
Typelist< Args... > Indices
Definition oraltypes.h:145
constexpr size_t SeqSize
Definition oral.h:109
constexpr detail::GroupBy< Ptrs... > GroupBy
Definition oral.h:975
ObjectInfo< T > Adapt(const QSqlDatabase &db)
Definition oral.h:1641
std::unique_ptr< ObjectInfo< T > > ObjectInfo_ptr
Definition oral.h:1662
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:132
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:1630
detail::SelectWrapper< T, detail::SelectBehaviour::Some > Select
Definition oral.h:1633
detail::AdaptDelete< T > Delete
Definition oral.h:1631
detail::AdaptInsert< T > Insert
Definition oral.h:1629
detail::DeleteByFieldsWrapper< T > DeleteBy
Definition oral.h:1635
detail::SelectWrapper< T, detail::SelectBehaviour::One > SelectOne
Definition oral.h:1634
AdaptDelete(const QSqlDatabase &db) noexcept
Definition oral.h:468
static constexpr int Value
Definition oral.h:1049
static constexpr auto ResultBehaviour_v
Definition oral.h:1172
static constexpr auto ResultBehaviour_v
Definition oral.h:1174
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition oral.h:1238
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition oral.h:1205
static auto Initializer(const QSqlQuery &q, int startIdx)
Definition oral.h:1181
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition oral.h:1267