LeechCraft  0.6.70-18450-gabe19ee3b0
Modular cross-platform feature rich live environment.
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>
23 #include <util/sll/ctstringutils.h>
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, ...) \
45 namespace 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 
59 namespace 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>
154  constexpr auto FieldNameByPtr = MorphFieldName<MemberPtrStruct_t<Ptr>, MemberNameByPtr<Ptr>> ();
155 
156  template<auto Ptr>
157  constexpr auto QualifiedFieldNameByPtr = MemberPtrStruct_t<Ptr>::ClassName + "." + FieldNameByPtr<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>
167  struct Type2Name
168  {
169  constexpr auto operator() () const noexcept
170  {
171  if constexpr (detail::TypeNameCustomizedMember<T>)
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  {
242  if constexpr (detail::TypeNameCustomizedMember<T>)
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> {})
251  return Convert<typename T::value_type> (t);
252  else
253  return t;
254  }
255 
256  static T operator() (const QVariant& var) noexcept
257  {
258  if constexpr (detail::TypeNameCustomizedMember<T>)
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> {})
267  return Convert<typename T::value_type> (var);
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>)
357  return !HasType<NoAutogen> (AsTypelist_t<ValueAtC_t<Seq, PKeyIndex_v<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>
486  struct AdaptDelete
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 
524  enum class ExprType
525  {
526  ConstTrue,
527 
529  LeafData,
530 
531  Greater,
532  Less,
533  Equal,
534  Geq,
535  Leq,
536  Neq,
537 
538  Like,
539  ILike,
540 
541  And,
542  Or
543  };
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>
592  struct WrapDirect
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>
660  class ExprTree
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  {
821  return detail::FieldNameByPtr<Ptr>;
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)
848  class ExprTree<ExprType::LeafStaticPlaceholder, MemberPtrs<Ptrs...>, void>
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>
919  concept EitherIsExprTree = IsExprTree<L> || IsExprTree<R>;
920 
921  template<typename L, typename R>
922  requires EitherIsExprTree<L, 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 
967  namespace infix
968  {
971  }
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,
1022  Min,
1023  Max
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>
1058  constexpr bool IsSelector<AggregateType<Fun, Ptr>> = true;
1059 
1060  template<typename FunRetType, CtString Fun, auto Ptr>
1061  constexpr bool IsSelector<CustomFunctionType<FunRetType, Fun, Ptr>> = true;
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>
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 
1091  constexpr detail::SelectWhole all {};
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  {
1137  return Convert<UnwrapIndirect_t<MemberPtrType_t<Ptr>>> (var);
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 
1233  enum class ResultBehaviour
1234  {
1235  All,
1236  First,
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>
1304  struct HandleSelector {};
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>
1327  struct HandleSelector<T, MemberPtrs<Ptrs...>> : HSBaseAll
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  {
1340  constexpr static auto Fields = ReplaceAll<Fun, '@'> (QualifiedFieldNameByPtr<Ptr>);
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>
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 
1391  {
1393  return ResultBehaviour::First;
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  {
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  {
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>
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>
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>...) + ")";
1725  RunTextQuery (db, ToString<query> ());
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>
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  {
1761  }
1762 
1763  template<auto Name, typename ImplFactory, typename T>
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  {
1789  return AdaptCreateTableNamed<T::ClassName, ImplFactory, T> ();
1790  }
1791  }
1792 
1793  template<typename T>
1794  struct ObjectInfo
1795  {
1799 
1803 
1804  using ObjectType_t = T;
1805  };
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> ();
1815  RunTextQuery (db, ToString<query> ());
1816  }
1817 
1818  detail::AdaptCreateIndices<T> (db);
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 }
DeleteByFieldsWrapper(const QSqlDatabase &db) noexcept
Definition: oral.h:1635
constexpr auto QualifiedFieldNameByPtr
Definition: oral.h:157
constexpr auto ExtractReplaceFields(InsertAction::Replace::WholeType)
Definition: oral.h:363
requires EitherIsExprTree< L, R > auto operator<=(const L &left, const R &right) noexcept
Definition: oral.h:937
constexpr bool IsRelational(ExprType type) noexcept
Definition: oral.h:570
constexpr detail::AggregateType< detail::AggregateFunction::Count, Ptr > count
Definition: oral.h:1104
detail::DeleteByFieldsWrapper< T > DeleteBy
Definition: oral.h:1802
void operator()(const Seq &seq) requires HasPKey< Seq >
Definition: oral.h:502
constexpr detail::OrderBy< Orders... > OrderBy
Definition: oral.h:1117
constexpr auto HasAutogenPKey() noexcept
Definition: oral.h:354
constexpr auto LimitOffsetToString() noexcept
Definition: oral.h:1276
constexpr auto Convert
Definition: oral.h:234
uint64_t Count
Definition: oral.h:1124
SelectWrapperCommon(const QSqlDatabase &db) noexcept
Definition: oral.h:1252
constexpr auto Join(auto &&) noexcept
Definition: ctstringutils.h:17
static constexpr auto ToSql() noexcept
Definition: oral.h:672
constexpr auto operator()() const noexcept
Definition: oral.h:169
concept TypeNameCustomizedMember
Definition: oral.h:160
constexpr bool IsSelector
Definition: oral.h:1052
Open "Replace" dialog.
auto MemberFromVariant(const QVariant &var) noexcept
Definition: oral.h:1135
constexpr auto WrapSubexpr(auto subexpr)
Definition: oral.h:583
void AdaptPtrs(const QSqlDatabase &db, ObjectInfo_ptr< Ts > &... objects)
Definition: oral.h:1842
requires EitherIsExprTree< L, R > auto operator>=(const L &left, const R &right) noexcept
Definition: oral.h:951
constexpr detail::AggregateType< detail::AggregateFunction::Max, Ptr > max
Definition: oral.h:1110
static auto Initializer(const QSqlQuery &q, int startIdx)
Definition: oral.h:1314
constexpr auto JoinTup(auto &&stringsTuple, auto &&sep) noexcept
Definition: ctstringutils.h:30
constexpr detail::GroupBy< Ptrs... > GroupBy
Definition: oral.h:1120
constexpr auto ExprTreeToSql() noexcept
Definition: oral.h:1008
void AdaptCreateIndices(const QSqlDatabase &db)
Definition: oral.h:1730
requires IsSelector< L > &&IsSelector< R > SelectorUnion< L, R > operator+(L, R) noexcept
Definition: oral.h:1074
void BindExprTree(const Tree &tree, QSqlQuery &query)
Definition: oral.h:1014
static UTIL_DB_API void DumpError(const QSqlError &error)
Dumps the error to the qWarning() stream.
Definition: dblock.cpp:68
void BindLimitOffset(QSqlQuery &query, L limit, O offset) noexcept
Definition: oral.h:1295
concept EitherIsExprTree
Definition: oral.h:919
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition: oral.h:1407
constexpr auto FieldNameByPtr
Definition: oral.h:154
auto Build() const noexcept
Definition: oral.h:1493
STL namespace.
Q_DECL_IMPORT const QString Count
The new total event count (int).
constexpr auto GetFullSize(const Seq &seq)
Definition: oral.h:105
MemberTypeType_t< decltype(Ptr)> MemberPtrType_t
Definition: typegetter.h:79
~QueryException() noexcept=default
T InitializeFromQuery(const QSqlQuery &q, std::index_sequence< Indices... >, int startIdx) noexcept
Definition: oral.h:512
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptrs... > > tuple
Definition: oral.h:1086
static constexpr bool HasAdditionalTables() noexcept
Definition: oral.h:735
constexpr ExprTree(const std::tuple< Ts... > &t) noexcept
Definition: oral.h:752
constexpr auto CreateIndex(const QSqlDatabase &db, Index< Ptrs... >)
Definition: oral.h:1720
static constexpr auto Fields
Definition: oral.h:1373
Q_DECL_IMPORT const QString Tags
consteval int PKeyIndex()
Definition: oral.h:340
constexpr auto MapTy(Src< Vals... >, auto f)
Definition: oral.h:1714
std::unique_ptr< ObjectInfo< T > > ObjectInfo_ptr
Definition: oral.h:1833
decltype(GetFieldAt< I >(std::declval< T >())) FieldType_t
Definition: itemsmodel.h:102
requires EitherIsExprTree< L, R > auto operator!=(const L &left, const R &right) noexcept
Definition: oral.h:958
auto DoInsert(const Seq &seq, QSqlQuery &insertQuery, bool bindPrimaryKey)
Definition: oral.h:312
constexpr auto TypeToSql() noexcept
Definition: oral.h:546
std::shared_ptr< QSqlQuery > QSqlQuery_ptr
Definition: oral.h:61
constexpr auto ExtractConstraintFields(UniqueSubset< Ptrs... >)
Definition: oral.h:1737
constexpr auto GetTypes(std::index_sequence< Indices... >) noexcept
Definition: oral.h:1758
auto MakeExprTree(const L &left, const R &right) noexcept
Definition: oral.h:911
consteval auto GetFieldName()
Definition: oral.h:126
constexpr bool IsPKey
Definition: oral.h:293
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition: oral.h:1083
constexpr bool IsSelector< SelectWhole >
Definition: oral.h:1055
constexpr auto ReplaceTupleElemImpl(Tuple &&tuple, NewType &&arg, std::index_sequence< TupIdxs... >) noexcept
Definition: oral.h:1175
void BindValues(QSqlQuery &query) const noexcept
Definition: oral.h:646
static constexpr auto ToSql() noexcept
Definition: oral.h:637
Type
Describes the various types of XDG .desktop files.
Definition: itemtypes.h:19
constexpr detail::CustomFunctionType< FunRetType, Fun, Ptr > fun
Definition: oral.h:1113
static constexpr auto ResultBehaviour_v
Definition: oral.h:1306
constexpr auto SeqIndices
Definition: oral.h:133
detail::SQLite::ImplFactory SQLiteImplFactory
Definition: sqliteimpl.h:83
detail::AdaptUpdate< T > Update
Definition: oral.h:1797
constexpr auto AdaptCreateTableNamed() noexcept
Definition: oral.h:1764
QString ToString() noexcept
Definition: ctstring.h:154
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition: oral.h:1331
consteval auto MorphFieldName()
Definition: oral.h:115
static constexpr bool HasAdditionalTables() noexcept
Definition: oral.h:696
auto operator()(Seq &t, Action action={}) const
Definition: oral.h:394
requires EitherIsExprTree< L, R > auto operator>(const L &left, const R &right) noexcept
Definition: oral.h:944
constexpr int PKeyIndex_v
Definition: oral.h:348
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
Definition: oral.h:1836
constexpr detail::InfixBinary< detail::ExprType::ILike > ilike
Definition: oral.h:970
static constexpr auto AdditionalTables() noexcept
Definition: oral.h:690
consteval int PKeyIndexUnsafe()
Definition: oral.h:328
A somewhat "strong" typedef.
Definition: newtype.h:32
typename AsTypelist< T >::Result_t AsTypelist_t
Definition: typelist.h:140
void BindAtIndex(const Seq &seq, QSqlQuery &query, bool bindPrimaryKey)
Definition: oral.h:305
void BindValues(QSqlQuery &query) const noexcept
Definition: oral.h:722
AdaptUpdate(const QSqlDatabase &db) noexcept
Definition: oral.h:1662
detail::AdaptInsert< T > Insert
Definition: oral.h:1796
requires(!std::same_as< T, QVariant >) struct ConvertT
Definition: oral.h:237
auto ZipWith(const Container< T1 > &c1, const Container< T2 > &c2, F f)
Definition: prelude.h:37
std::decay_t< decltype(Get< Idx >(std::declval< Seq >()))> ValueAtC_t
Definition: oral.h:290
static constexpr auto AdditionalTables() noexcept
Definition: oral.h:729
static constexpr int Value
Definition: oral.h:1194
decltype(auto) constexpr Get(const Seq &seq)
Definition: oral.h:91
constexpr auto ReplaceTupleElem(std::tuple< TupleArgs... > &&tuple, NewType &&arg) noexcept
Definition: oral.h:1184
detail::SelectWrapper< T, detail::SelectBehaviour::Some > Select
Definition: oral.h:1800
auto Combine(std::tuple< LArgs... > &&left, std::tuple< RArgs... > &&right) noexcept
Definition: oral.h:1210
constexpr detail::InfixBinary< detail::ExprType::Like > like
Definition: oral.h:969
constexpr auto MemberNameByPtr
Definition: oral.h:86
auto Tup2 &&tup2 noexcept
Definition: ctstringutils.h:68
decltype(auto) HandleResultBehaviour(ResList &&list) noexcept
Definition: oral.h:1240
constexpr detail::MemberPtrs< Ptrs... > fields
Definition: oral.h:1089
static constexpr auto ResultBehaviour_v
Definition: oral.h:1307
QSqlQuery RunTextQuery(const QSqlDatabase &db, const QString &text)
Runs the given query text on the given db.
Definition: util.cpp:22
constexpr auto operator,(const AssignList< OL, OR > &tail) noexcept
Definition: oral.h:653
constexpr detail::AggregateType< detail::AggregateFunction::Min, Ptr > min
Definition: oral.h:1107
requires EitherIsExprTree< L, R > auto operator &&(const L &left, const R &right) noexcept
Definition: oral.h:995
constexpr auto AsLeafData(const T &node) noexcept
Definition: oral.h:789
constexpr size_t SeqSize
Definition: oral.h:75
typename std::conditional_t< IsIndirect< T > {}, T, WrapDirect< T > >::value_type UnwrapIndirect_t
Definition: oral.h:600
MemberTypeStruct_t< decltype(Ptr)> MemberPtrStruct_t
Definition: typegetter.h:82
static auto Initializer(const QSqlQuery &q, int startIdx) noexcept
Definition: oral.h:1375
constexpr ExprTree(const L &l, const R &r) noexcept
Definition: oral.h:665
constexpr auto MemberNameByIdx
Definition: oral.h:83
constexpr auto FieldNames
Definition: oral.h:136
ObjectInfo< T > Adapt(const QSqlDatabase &db)
Definition: oral.h:1808
decltype(auto) constexpr GetReplaceTupleElem(Tuple &&tuple, NewType &&arg) noexcept
Definition: oral.h:1166
detail::SelectWrapper< T, detail::SelectBehaviour::One > SelectOne
Definition: oral.h:1801
auto RunQuery(const QString &queryStr, auto &&binder) const
Definition: oral.h:1257
constexpr auto Nub()
Definition: ctstringutils.h:88
constexpr detail::SelectWhole all
Definition: oral.h:1091
constexpr AssignList(const L &l, const R &r) noexcept
Definition: oral.h:630
Typelist< Args... > Constraints
Definition: oraltypes.h:139
requires EitherIsExprTree< L, R > auto operator||(const L &left, const R &right) noexcept
Definition: oral.h:1002
constexpr auto GetConstraintsStrings() noexcept
Definition: oral.h:1749
requires EitherIsExprTree< L, R > auto operator<(const L &left, const R &right) noexcept
Definition: oral.h:930
detail::AdaptDelete< T > Delete
Definition: oral.h:1798
requires EitherIsExprTree< L, R > auto operator==(const L &left, const R &right) noexcept
Definition: oral.h:923
constexpr auto FullSize
Definition: oral.h:112
constexpr auto QualifiedFieldNames
Definition: oral.h:148
auto operator|(const L &left, InfixBinary< Op >) noexcept
Definition: oral.h:982
Typelist< Args... > Indices
Definition: oraltypes.h:145
constexpr auto BoundFieldNames
Definition: oral.h:142
void BindValues(QSqlQuery &query) const noexcept
Definition: oral.h:683
constexpr bool BeenAdapted
Definition: oral.h:72
constexpr auto CombineBehaviour(ResultBehaviour l, ResultBehaviour r) noexcept
Definition: oral.h:1390
auto MakeIndexedQueryHandler(const QSqlQuery &q, int startIdx=0) noexcept
Definition: oral.h:1141
constexpr auto AdaptCreateTable() noexcept
Definition: oral.h:1787
constexpr bool IsExprTree
Definition: oral.h:619
std::tuple_element_t< 0, detail::CallTypeGetter_t< F > > RetType_t
Definition: typegetter.h:52
QVariant ToVariantF(const T &t) noexcept
Definition: oral.h:299
concept BaseTypeCustomized
Definition: oral.h:163
constexpr auto ConstTrueTree_v
Definition: oral.h:908
AdaptDelete(const QSqlDatabase &db) noexcept
Definition: oral.h:490
AdaptInsert(const QSqlDatabase &db) noexcept
Definition: oral.h:388
constexpr bool Typecheck()
Definition: oral.h:603
A proper void type, akin to unit (or ()) type in functional languages.
Definition: void.h:20