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