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