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