LeechCraft  0.6.70-13729-g7046a9d2a7
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  * Boost Software License - Version 1.0 - August 17th, 2003
6  *
7  * Permission is hereby granted, free of charge, to any person or organization
8  * obtaining a copy of the software and accompanying documentation covered by
9  * this license (the "Software") to use, reproduce, display, distribute,
10  * execute, and transmit the Software, and to prepare derivative works of the
11  * Software, and to permit third-parties to whom the Software is furnished to
12  * do so, all subject to the following:
13  *
14  * The copyright notices in the Software and this entire statement, including
15  * the above license grant, this restriction and the following disclaimer,
16  * must be included in all copies of the Software, in whole or in part, and
17  * all derivative works of the Software, unless such copies or derivative
18  * works are solely in the form of machine-executable object code generated by
19  * a source language processor.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  **********************************************************************/
29 
30 #pragma once
31 
32 #include <stdexcept>
33 #include <type_traits>
34 #include <memory>
35 #include <optional>
36 #include <boost/fusion/include/for_each.hpp>
37 #include <boost/fusion/include/fold.hpp>
38 #include <boost/fusion/include/filter_if.hpp>
39 #include <boost/fusion/container/vector.hpp>
40 #include <boost/fusion/include/vector.hpp>
41 #include <boost/fusion/include/transform.hpp>
42 #include <boost/fusion/include/zip.hpp>
43 #include <boost/fusion/container/generation/make_vector.hpp>
44 #include <QStringList>
45 #include <QDateTime>
46 #include <QPair>
47 #include <QSqlQuery>
48 #include <QSqlRecord>
49 #include <QVariant>
50 #include <QDateTime>
51 #include <QtDebug>
52 #include <util/sll/qtutil.h>
53 #include <util/sll/prelude.h>
54 #include <util/sll/typelist.h>
55 #include <util/sll/typelevel.h>
56 #include <util/sll/typegetter.h>
57 #include <util/sll/detector.h>
58 #include <util/sll/unreachable.h>
59 #include <util/sll/void.h>
60 #include <util/db/dblock.h>
61 #include <util/db/util.h>
62 #include "oraltypes.h"
63 #include "oraldetailfwd.h"
64 #include "impldefs.h"
65 #include "sqliteimpl.h"
66 
67 namespace LC
68 {
69 namespace Util
70 {
71 namespace oral
72 {
73  using QSqlQuery_ptr = std::shared_ptr<QSqlQuery>;
74 
75  class QueryException : public std::runtime_error
76  {
77  const QSqlQuery_ptr Query_;
78  public:
79  QueryException (const std::string& str, const QSqlQuery_ptr& q)
80  : std::runtime_error (str)
81  , Query_ (q)
82  {
83  }
84 
85  ~QueryException () noexcept = default;
86 
87  const QSqlQuery_ptr& GetQueryPtr () const
88  {
89  return Query_;
90  }
91 
92  const QSqlQuery& GetQuery () const
93  {
94  return *Query_;
95  }
96  };
97 
98  namespace detail
99  {
100  template<typename U>
101  using MorpherDetector = decltype (std::declval<U> ().FieldNameMorpher (QString {}));
102 
103  template<typename T>
104  QString MorphFieldName (QString str) noexcept
105  {
106  if constexpr (IsDetected_v<MorpherDetector, T>)
107  return T::FieldNameMorpher (str);
108  else
109  {
110  if (str.endsWith ('_'))
111  str.chop (1);
112  return str;
113  }
114  }
115 
116  template<typename Seq, int Idx>
117  QString GetFieldName () noexcept
118  {
119  return MorphFieldName<Seq> (boost::fusion::extension::struct_member_name<Seq, Idx>::call ());
120  }
121 
122  template<typename S>
123  constexpr auto SeqSize = boost::fusion::result_of::size<S>::type::value;
124 
125  template<typename S>
126  constexpr auto SeqIndices = std::make_index_sequence<SeqSize<S>> {};
127 
128  template<typename S>
130  {
131  QStringList operator() () const noexcept
132  {
133  return Run (SeqIndices<S>);
134  }
135  private:
136  template<size_t... Vals>
137  QStringList Run (std::index_sequence<Vals...>) const noexcept
138  {
139  return { GetFieldName<S, Vals> ()... };
140  }
141  };
142 
143  template<typename S>
144  struct AddressOf
145  {
146  inline static S Obj_ {};
147 
148  template<auto P>
149  static constexpr auto Ptr () noexcept
150  {
151  return &(Obj_.*P);
152  }
153 
154  template<int Idx>
155  static constexpr auto Index () noexcept
156  {
157  return &boost::fusion::at_c<Idx> (Obj_);
158  }
159  };
160 
161  template<auto Ptr, size_t Idx = 0>
162  constexpr size_t FieldIndex () noexcept
163  {
164  using S = MemberPtrStruct_t<Ptr>;
165 
166  if constexpr (Idx == SeqSize<S>)
167  return -1;
168  else
169  {
170  constexpr auto direct = AddressOf<S>::template Ptr<Ptr> ();
171  constexpr auto indexed = AddressOf<S>::template Index<Idx> ();
172  if constexpr (std::is_same_v<decltype (direct), decltype (indexed)>)
173  {
174  if (indexed == direct)
175  return Idx;
176  }
177 
178  return FieldIndex<Ptr, Idx + 1> ();
179  }
180  }
181 
182  template<auto Ptr>
183  QString GetFieldNamePtr () noexcept
184  {
185  using S = MemberPtrStruct_t<Ptr>;
186  return GetFieldName<S, FieldIndex<Ptr> ()> ();
187  }
188 
189  template<auto Ptr>
190  QString GetQualifiedFieldNamePtr () noexcept
191  {
192  using S = MemberPtrStruct_t<Ptr>;
193  return S::ClassName () + "." + GetFieldName<S, FieldIndex<Ptr> ()> ();
194  }
195 
196  template<typename T>
197  using TypeNameDetector = decltype (T::TypeName);
198 
199  template<typename T>
200  constexpr bool TypeNameCustomized = IsDetected_v<TypeNameDetector, T>;
201 
202  template<typename T>
203  using BaseTypeDetector = typename T::BaseType;
204 
205  template<typename T>
206  constexpr bool BaseTypeCustomized = IsDetected_v<BaseTypeDetector, T>;
207  }
208 
209  template<typename ImplFactory, typename T, typename = void>
210  struct Type2Name
211  {
212  QString operator() () const noexcept
213  {
214  if constexpr (HasType<T> (Typelist<int, qlonglong, qulonglong, bool> {}) || std::is_enum_v<T>)
215  return "INTEGER";
216  else if constexpr (std::is_same_v<T, double>)
217  return "REAL";
218  else if constexpr (std::is_same_v<T, QString> || std::is_same_v<T, QDateTime> || std::is_same_v<T, QUrl>)
219  return "TEXT";
220  else if constexpr (std::is_same_v<T, QByteArray>)
221  return ImplFactory::TypeLits::Binary;
222  else if constexpr (detail::TypeNameCustomized<T>)
223  return T::TypeName;
224  else if constexpr (detail::BaseTypeCustomized<T>)
226  else
227  static_assert (std::is_same_v<T, struct Dummy>, "Unsupported type");
228  }
229  };
230 
231  template<typename ImplFactory, typename T>
232  struct Type2Name<ImplFactory, Unique<T>>
233  {
234  QString operator() () const noexcept { return Type2Name<ImplFactory, T> () () + " UNIQUE"; }
235  };
236 
237  template<typename ImplFactory, typename T>
238  struct Type2Name<ImplFactory, NotNull<T>>
239  {
240  QString operator() () const noexcept { return Type2Name<ImplFactory, T> () () + " NOT NULL"; }
241  };
242 
243  template<typename ImplFactory, typename T, typename... Tags>
244  struct Type2Name<ImplFactory, PKey<T, Tags...>>
245  {
246  QString operator() () const noexcept { return Type2Name<ImplFactory, T> () () + " PRIMARY KEY"; }
247  };
248 
249  template<typename ImplFactory, typename... Tags>
250  struct Type2Name<ImplFactory, PKey<int, Tags...>>
251  {
252  QString operator() () const noexcept { return ImplFactory::TypeLits::IntAutoincrement; }
253  };
254 
255  template<typename ImplFactory, auto Ptr>
256  struct Type2Name<ImplFactory, References<Ptr>>
257  {
258  QString operator() () const noexcept
259  {
260  const auto& className = MemberPtrStruct_t<Ptr>::ClassName ();
262  " REFERENCES " + className + " (" + detail::GetFieldNamePtr<Ptr> () + ") ON DELETE CASCADE";
263  }
264  };
265 
266  template<typename T, typename = void>
267  struct ToVariant
268  {
269  QVariant operator() (const T& t) const noexcept
270  {
271  if constexpr (std::is_same_v<T, QDateTime>)
272  return t.toString (Qt::ISODate);
273  else if constexpr (std::is_enum_v<T>)
274  return static_cast<qint64> (t);
275  else if constexpr (IsIndirect<T> {})
276  return ToVariant<typename T::value_type> {} (t);
277  else if constexpr (detail::TypeNameCustomized<T>)
278  return t.ToVariant ();
279  else if constexpr (detail::BaseTypeCustomized<T>)
280  return ToVariant<typename T::BaseType> {} (t.ToBaseType ());
281  else
282  return t;
283  }
284  };
285 
286  template<typename T, typename = void>
287  struct FromVariant
288  {
289  T operator() (const QVariant& var) const noexcept
290  {
291  if constexpr (std::is_same_v<T, QDateTime>)
292  return QDateTime::fromString (var.toString (), Qt::ISODate);
293  else if constexpr (std::is_enum_v<T>)
294  return static_cast<T> (var.value<qint64> ());
295  else if constexpr (IsIndirect<T> {})
296  return FromVariant<typename T::value_type> {} (var);
297  else if constexpr (detail::TypeNameCustomized<T>)
298  return T::FromVariant (var);
299  else if constexpr (detail::BaseTypeCustomized<T>)
300  return T::FromBaseType (FromVariant<typename T::BaseType> {} (var));
301  else
302  return var.value<T> ();
303  }
304  };
305 
306  namespace detail
307  {
308  template<typename T>
309  struct IsPKey : std::false_type {};
310 
311  template<typename U, typename... Tags>
312  struct IsPKey<PKey<U, Tags...>> : std::true_type {};
313 
314  template<typename T>
315  QVariant ToVariantF (const T& t) noexcept
316  {
317  return ToVariant<T> {} (t);
318  }
319 
320  template<typename T>
321  auto MakeInserter (const CachedFieldsData& data, const QSqlQuery_ptr& insertQuery, bool bindPrimaryKey) noexcept
322  {
323  return [data, insertQuery, bindPrimaryKey] (const T& t)
324  {
325  boost::fusion::fold (t, data.BoundFields_.begin (),
326  [&] (auto pos, const auto& elem)
327  {
328  using Elem = std::decay_t<decltype (elem)>;
329  if (bindPrimaryKey || !IsPKey<Elem>::value)
330  insertQuery->bindValue (*pos++, ToVariantF (elem));
331  return pos;
332  });
333 
334  if (!insertQuery->exec ())
335  {
336  DBLock::DumpError (*insertQuery);
337  throw QueryException ("insert query execution failed", insertQuery);
338  }
339  };
340  }
341 
342  template<typename Seq, int Idx>
343  using ValueAtC_t = typename boost::fusion::result_of::value_at_c<Seq, Idx>::type;
344 
345  template<typename Seq, typename Idx>
346  using ValueAt_t = typename boost::fusion::result_of::value_at<Seq, Idx>::type;
347 
348  template<typename Seq, typename MemberIdx = boost::mpl::int_<0>>
349  struct FindPKey
350  {
351  static_assert ((boost::fusion::result_of::size<Seq>::value) != (MemberIdx::value),
352  "Primary key not found");
353 
354  template<typename T>
355  struct Lazy
356  {
357  using type = T;
358  };
359 
360  using result_type = typename std::conditional_t<
364  >::type;
365  };
366 
367  template<typename Seq>
368  using FindPKeyDetector = boost::mpl::int_<FindPKey<Seq>::result_type::value>;
369 
370  template<typename Seq>
371  constexpr auto HasPKey = IsDetected_v<FindPKeyDetector, Seq>;
372 
373  template<typename Seq>
374  constexpr auto HasAutogenPKey () noexcept
375  {
376  if constexpr (HasPKey<Seq>)
377  return !HasType<NoAutogen> (AsTypelist_t<ValueAtC_t<Seq, FindPKey<Seq>::result_type::value>> {});
378  else
379  return false;
380  }
381 
382  template<typename T>
383  CachedFieldsData BuildCachedFieldsData (const QString& table) noexcept
384  {
385  const auto& fields = detail::GetFieldsNames<T> {} ();
386  const auto& qualified = Util::Map (fields, [&table] (const QString& field) { return table + "." + field; });
387  const auto& boundFields = Util::Map (fields, [] (const QString& str) { return ':' + str; });
388 
389  return { table, fields, qualified, boundFields };
390  }
391 
392  template<typename T>
394  {
395  static CachedFieldsData result = BuildCachedFieldsData<T> (T::ClassName ());
396  return result;
397  }
398 
399  template<typename Seq>
401  {
402  const QSqlDatabase DB_;
403  const CachedFieldsData Data_;
404 
405  constexpr static bool HasAutogen_ = HasAutogenPKey<Seq> ();
406 
407  IInsertQueryBuilder_ptr QueryBuilder_;
408  public:
409  template<typename ImplFactory>
410  AdaptInsert (const QSqlDatabase& db, CachedFieldsData data, ImplFactory&& factory) noexcept
411  : Data_ { RemovePKey (data) }
412  , QueryBuilder_ { factory.MakeInsertQueryBuilder (db, Data_) }
413  {
414  }
415 
416  auto operator() (Seq& t, InsertAction action = InsertAction::Default) const
417  {
418  return Run<true> (t, action);
419  }
420 
421  auto operator() (const Seq& t, InsertAction action = InsertAction::Default) const
422  {
423  return Run<false> (t, action);
424  }
425  private:
426  template<bool UpdatePKey, typename Val>
427  auto Run (Val&& t, InsertAction action) const
428  {
429  const auto query = QueryBuilder_->GetQuery (action);
430 
431  MakeInserter<Seq> (Data_, query, !HasAutogen_) (t);
432 
433  if constexpr (HasAutogen_)
434  {
435  constexpr auto index = FindPKey<Seq>::result_type::value;
436 
437  const auto& lastId = FromVariant<ValueAtC_t<Seq, index>> {} (query->lastInsertId ());
438  if constexpr (UpdatePKey)
439  boost::fusion::at_c<index> (t) = lastId;
440  else
441  return lastId;
442  }
443  }
444 
445  static CachedFieldsData RemovePKey (CachedFieldsData data) noexcept
446  {
447  if constexpr (HasAutogen_)
448  {
449  constexpr auto index = FindPKey<Seq>::result_type::value;
450  data.Fields_.removeAt (index);
451  data.BoundFields_.removeAt (index);
452  }
453  return data;
454  }
455  };
456 
457  template<typename Seq, bool HasPKey = HasPKey<Seq>>
458  struct AdaptDelete
459  {
460  std::function<void (Seq)> Deleter_;
461  public:
462  template<bool B = HasPKey>
463  AdaptDelete (const QSqlDatabase& db, const CachedFieldsData& data, std::enable_if_t<B>* = nullptr) noexcept
464  {
465  const auto index = FindPKey<Seq>::result_type::value;
466 
467  const auto& boundName = data.BoundFields_.at (index);
468  const auto& del = "DELETE FROM " + data.Table_ +
469  " WHERE " + data.Fields_.at (index) + " = " + boundName;
470 
471  const auto deleteQuery = std::make_shared<QSqlQuery> (db);
472  deleteQuery->prepare (del);
473 
474  Deleter_ = [deleteQuery, boundName] (const Seq& t)
475  {
476  constexpr auto index = FindPKey<Seq>::result_type::value;
477  deleteQuery->bindValue (boundName, ToVariantF (boost::fusion::at_c<index> (t)));
478  if (!deleteQuery->exec ())
479  throw QueryException ("delete query execution failed", deleteQuery);
480  };
481  }
482 
483  template<bool B = HasPKey>
484  AdaptDelete (const QSqlDatabase&, const CachedFieldsData&, std::enable_if_t<!B>* = nullptr) noexcept
485  {
486  }
487 
488  template<bool B = HasPKey>
489  std::enable_if_t<B> operator() (const Seq& seq)
490  {
491  Deleter_ (seq);
492  }
493  };
494 
495  template<typename T, typename... Args>
496  using AggregateDetector_t = decltype (new T { std::declval<Args> ()... });
497 
498  template<typename T, size_t... Indices>
499  T InitializeFromQuery (const QSqlQuery& q, std::index_sequence<Indices...>, int startIdx) noexcept
500  {
502  return T { FromVariant<ValueAtC_t<T, Indices>> {} (q.value (startIdx + Indices))... };
503  else
504  {
505  T t;
506  const auto dummy = std::initializer_list<int>
507  {
508  (static_cast<void> (boost::fusion::at_c<Indices> (t) = FromVariant<ValueAtC_t<T, Indices>> {} (q.value (startIdx + Indices))), 0)...
509  };
510  Q_UNUSED (dummy);
511  return t;
512  }
513  }
514 
515  enum class ExprType
516  {
517  ConstTrue,
518 
520  LeafData,
521 
522  Greater,
523  Less,
524  Equal,
525  Geq,
526  Leq,
527  Neq,
528 
529  Like,
530 
531  And,
532  Or
533  };
534 
535  inline QString TypeToSql (ExprType type) noexcept
536  {
537  switch (type)
538  {
539  case ExprType::Greater:
540  return ">";
541  case ExprType::Less:
542  return "<";
543  case ExprType::Equal:
544  return "=";
545  case ExprType::Geq:
546  return ">=";
547  case ExprType::Leq:
548  return "<=";
549  case ExprType::Neq:
550  return "!=";
551  case ExprType::Like:
552  return "LIKE";
553  case ExprType::And:
554  return "AND";
555  case ExprType::Or:
556  return "OR";
557 
559  case ExprType::LeafData:
560  case ExprType::ConstTrue:
561  return "invalid type";
562  }
563 
565  }
566 
567  constexpr bool IsRelational (ExprType type) noexcept
568  {
569  return type == ExprType::Greater ||
570  type == ExprType::Less ||
571  type == ExprType::Equal ||
572  type == ExprType::Geq ||
573  type == ExprType::Leq ||
574  type == ExprType::Neq ||
575  type == ExprType::Like;
576  }
577 
578  template<typename T>
579  struct ToSqlState
580  {
581  int LastID_;
582  QVariantMap BoundMembers_;
583  };
584 
585  template<typename T>
586  struct WrapDirect
587  {
588  using value_type = T;
589  };
590 
591  template<typename T>
592  using UnwrapIndirect_t = typename std::conditional_t<IsIndirect<T> {},
593  T,
594  WrapDirect<T>>::value_type;
595 
596  template<typename Seq, typename L, typename R>
597  using ComparableDetector = decltype (std::declval<UnwrapIndirect_t<typename L::template ValueType_t<Seq>>> () ==
598  std::declval<UnwrapIndirect_t<typename R::template ValueType_t<Seq>>> ());
599 
600  template<typename Seq, typename L, typename R>
601  constexpr auto AreComparableTypes = IsDetected_v<ComparableDetector, Seq, L, R> || IsDetected_v<ComparableDetector, Seq, R, L>;
602 
603  template<typename Seq, typename L, typename R, typename = void>
604  struct RelationalTypesCheckerBase : std::false_type {};
605 
606  template<typename Seq, typename L, typename R>
607  struct RelationalTypesCheckerBase<Seq, L, R, std::enable_if_t<AreComparableTypes<Seq, L, R>>> : std::true_type {};
608 
609  template<ExprType Type, typename Seq, typename L, typename R, typename = void>
610  struct RelationalTypesChecker : std::true_type {};
611 
612  template<ExprType Type, typename Seq, typename L, typename R>
613  struct RelationalTypesChecker<Type, Seq, L, R, std::enable_if_t<IsRelational (Type)>> : RelationalTypesCheckerBase<Seq, L, R> {};
614 
615  template<ExprType Type, typename L = void, typename R = void>
616  class ExprTree;
617 
618  template<typename T>
619  struct IsExprTree : std::false_type {};
620 
621  template<ExprType Type, typename L, typename R>
622  struct IsExprTree<ExprTree<Type, L, R>> : std::true_type {};
623 
624  template<typename L, typename R>
626  {
627  L Left_;
628  R Right_;
629  public:
630  AssignList (const L& l, const R& r) noexcept
631  : Left_ { l }
632  , Right_ { r }
633  {
634  }
635 
636  template<typename T>
637  QString ToSql (ToSqlState<T>& state) const noexcept
638  {
639  if constexpr (IsExprTree<L> {})
640  return Left_.GetFieldName () + " = " + Right_.ToSql (state);
641  else
642  return Left_.ToSql (state) + ", " + Right_.ToSql (state);
643  }
644 
645  template<typename OL, typename OR>
646  auto operator, (const AssignList<OL, OR>& tail) noexcept
647  {
648  return AssignList<AssignList<L, R>, AssignList<OL, OR>> { *this, tail };
649  }
650  };
651 
652  template<ExprType Type, typename L, typename R>
653  class ExprTree
654  {
655  L Left_;
656  R Right_;
657  public:
658  ExprTree (const L& l, const R& r) noexcept
659  : Left_ (l)
660  , Right_ (r)
661  {
662  }
663 
664  template<typename T>
665  QString ToSql (ToSqlState<T>& state) const noexcept
666  {
668  "Incompatible types passed to a relational operator.");
669 
670  return Left_.ToSql (state) + " " + TypeToSql (Type) + " " + Right_.ToSql (state);
671  }
672 
673  template<typename T>
674  QSet<QString> AdditionalTables () const noexcept
675  {
676  return Left_.template AdditionalTables<T> () + Right_.template AdditionalTables<T> ();
677  }
678 
679  template<typename T>
680  constexpr static bool HasAdditionalTables () noexcept
681  {
682  return L::template HasAdditionalTables<T> () || R::template HasAdditionalTables<T> ();
683  }
684  };
685 
686  template<typename T>
687  class ExprTree<ExprType::LeafData, T, void>
688  {
689  T Data_;
690  public:
691  template<typename>
692  using ValueType_t = T;
693 
694  ExprTree (const T& t) noexcept
695  : Data_ (t)
696  {
697  }
698 
699  template<typename ObjT>
700  QString ToSql (ToSqlState<ObjT>& state) const noexcept
701  {
702  const auto& name = ":bound_" + QString::number (++state.LastID_);
703  state.BoundMembers_ [name] = ToVariantF (Data_);
704  return name;
705  }
706 
707  template<typename>
708  QSet<QString> AdditionalTables () const noexcept
709  {
710  return {};
711  }
712 
713  template<typename>
714  constexpr static bool HasAdditionalTables () noexcept
715  {
716  return false;
717  }
718  };
719 
720  template<typename T>
721  constexpr auto AsLeafData (const T& node) noexcept
722  {
723  if constexpr (IsExprTree<T> {})
724  return node;
725  else
726  return ExprTree<ExprType::LeafData, T> { node };
727  }
728 
729  template<auto... Ptr>
730  struct MemberPtrs {};
731 
732  template<auto Ptr>
734  {
735  using ExpectedType_t = MemberPtrType_t<Ptr>;
736  public:
737  template<typename>
738  using ValueType_t = ExpectedType_t;
739 
740  template<typename T>
741  QString ToSql (ToSqlState<T>&) const noexcept
742  {
743  return MemberPtrStruct_t<Ptr>::ClassName () + "." + GetFieldName ();
744  }
745 
746  QString GetFieldName () const noexcept
747  {
748  return detail::GetFieldNamePtr<Ptr> ();
749  }
750 
751  template<typename T>
752  QSet<QString> AdditionalTables () const noexcept
753  {
754  using Seq = MemberPtrStruct_t<Ptr>;
755  if constexpr (std::is_same_v<Seq, T>)
756  return {};
757  else
758  return { Seq::ClassName () };
759  }
760 
761  template<typename T>
762  constexpr static bool HasAdditionalTables () noexcept
763  {
764  return !std::is_same_v<MemberPtrStruct_t<Ptr>, T>;
765  }
766 
767  auto operator= (const ExpectedType_t& r) const noexcept
768  {
769  return AssignList { *this, AsLeafData (r) };
770  }
771  };
772 
773  template<>
774  class ExprTree<ExprType::ConstTrue, void, void> {};
775 
777 
778  template<ExprType Type, typename L, typename R>
779  auto MakeExprTree (const L& left, const R& right) noexcept
780  {
781  using EL = decltype (AsLeafData (left));
782  using ER = decltype (AsLeafData (right));
783  return ExprTree<Type, EL, ER> { AsLeafData (left), AsLeafData (right) };
784  }
785 
786  template<typename L, typename R>
787  constexpr bool EitherIsExprTree () noexcept
788  {
789  if (IsExprTree<L> {})
790  return true;
791  if (IsExprTree<R> {})
792  return true;
793  return false;
794  }
795 
796  template<typename L, typename R>
797  using EnableRelOp_t = std::enable_if_t<EitherIsExprTree<L, R> ()>;
798 
799  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
800  auto operator< (const L& left, const R& right) noexcept
801  {
802  return MakeExprTree<ExprType::Less> (left, right);
803  }
804 
805  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
806  auto operator> (const L& left, const R& right) noexcept
807  {
808  return MakeExprTree<ExprType::Greater> (left, right);
809  }
810 
811  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
812  auto operator== (const L& left, const R& right) noexcept
813  {
814  return MakeExprTree<ExprType::Equal> (left, right);
815  }
816 
817  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
818  auto operator!= (const L& left, const R& right) noexcept
819  {
820  return MakeExprTree<ExprType::Neq> (left, right);
821  }
822 
823  template<ExprType Op>
824  struct InfixBinary {};
825  }
826 
827  namespace infix
828  {
830  }
831 
832  namespace detail
833  {
834  template<typename L, ExprType Op>
836  {
837  const L& Left_;
838  };
839 
840  template<typename L, ExprType Op>
841  auto operator| (const L& left, InfixBinary<Op>) noexcept
842  {
843  return InfixBinaryProxy<L, Op> { left };
844  }
845 
846  template<typename L, ExprType Op, typename R>
847  auto operator| (const InfixBinaryProxy<L, Op>& left, const R& right) noexcept
848  {
849  return MakeExprTree<Op> (left.Left_, right);
850  }
851 
852  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
853  auto operator&& (const L& left, const R& right) noexcept
854  {
855  return MakeExprTree<ExprType::And> (left, right);
856  }
857 
858  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
859  auto operator|| (const L& left, const R& right) noexcept
860  {
861  return MakeExprTree<ExprType::Or> (left, right);
862  }
863 
864  template<typename F>
866  {
867  QString Sql_;
869  int LastID_;
870 
871  ExprTreeHandler (const QString& sql, F&& binder, int lastId) noexcept
872  : Sql_ { sql }
873  , Binder_ { std::move (binder) }
874  , LastID_ { lastId }
875  {
876  }
877  };
878 
879  template<typename>
880  auto HandleExprTree (const ExprTree<ExprType::ConstTrue>&, int lastId = 0) noexcept
881  {
882  return ExprTreeHandler { "1 = 1", [] (auto&&) {}, lastId };
883  }
884 
885  template<typename Seq, typename Tree,
886  typename = decltype (std::declval<Tree> ().ToSql (std::declval<ToSqlState<Seq>&> ()))>
887  auto HandleExprTree (const Tree& tree, int lastId = 0) noexcept
888  {
889  ToSqlState<Seq> state { lastId, {} };
890 
891  const auto& sql = tree.ToSql (state);
892 
893  return ExprTreeHandler
894  {
895  sql,
896  [state] (QSqlQuery& query)
897  {
898  for (const auto& pair : Stlize (state.BoundMembers_))
899  query.bindValue (pair.first, pair.second);
900  },
901  state.LastID_
902  };
903  }
904 
905  enum class AggregateFunction
906  {
907  Count,
908  Min,
909  Max
910  };
911 
912  template<AggregateFunction, auto Ptr>
913  struct AggregateType {};
914 
915  struct CountAll {};
916 
917  inline constexpr CountAll *CountAllPtr = nullptr;
918 
919  template<typename... MemberDirectionList>
920  struct OrderBy {};
921 
922  template<auto... Ptrs>
923  struct GroupBy {};
924 
925  struct SelectWhole {};
926 
927  template<typename L, typename R>
928  struct SelectorUnion {};
929 
930  template<typename T>
931  struct IsSelector : std::false_type {};
932 
933  template<>
934  struct IsSelector<SelectWhole> : std::true_type {};
935 
936  template<AggregateFunction Fun, auto Ptr>
937  struct IsSelector<AggregateType<Fun, Ptr>> : std::true_type {};
938 
939  template<auto... Ptrs>
940  struct IsSelector<MemberPtrs<Ptrs...>> : std::true_type {};
941 
942  template<typename L, typename R>
943  struct IsSelector<SelectorUnion<L, R>> : std::true_type {};
944 
945  template<typename L, typename R, typename = std::enable_if_t<IsSelector<L> {} && IsSelector<R> {}>>
947  {
948  return {};
949  }
950  }
951 
952  namespace sph
953  {
954  template<auto Ptr>
956 
957  template<auto... Ptrs>
958  constexpr detail::MemberPtrs<Ptrs...> fields {};
959 
960  constexpr detail::SelectWhole all {};
961 
962  template<auto... Ptrs>
963  struct asc {};
964 
965  template<auto... Ptrs>
966  struct desc {};
967 
968  template<auto Ptr = detail::CountAllPtr>
970 
971  template<auto Ptr>
973 
974  template<auto Ptr>
976  };
977 
978  template<typename... Orders>
979  constexpr detail::OrderBy<Orders...> OrderBy {};
980 
981  template<auto... Ptrs>
982  constexpr detail::GroupBy<Ptrs...> GroupBy {};
983 
984  struct Limit
985  {
986  uint64_t Count;
987 
988  Limit (uint64_t count) noexcept
989  : Count { count }
990  {
991  }
992  };
993 
994  struct Offset
995  {
996  uint64_t Count;
997 
998  Offset (uint64_t count) noexcept
999  : Count { count }
1000  {
1001  }
1002  };
1003 
1004  namespace detail
1005  {
1006  template<auto... Ptrs, size_t... Idxs>
1007  auto MakeIndexedQueryHandler (MemberPtrs<Ptrs...>, std::index_sequence<Idxs...>) noexcept
1008  {
1009  return [] (const QSqlQuery& q, int startIdx = 0) noexcept
1010  {
1011  if constexpr (sizeof... (Ptrs) == 1)
1012  return FromVariant<UnwrapIndirect_t<Head_t<Typelist<MemberPtrType_t<Ptrs>...>>>> {} (q.value (startIdx));
1013  else
1014  return std::tuple { FromVariant<UnwrapIndirect_t<MemberPtrType_t<Ptrs>>> {} (q.value (startIdx + Idxs))... };
1015  };
1016  }
1017 
1018  template<auto Ptr>
1019  auto MakeIndexedQueryHandler () noexcept
1020  {
1021  return [] (const QSqlQuery& q, int startIdx = 0) noexcept
1022  {
1023  return FromVariant<UnwrapIndirect_t<MemberPtrType_t<Ptr>>> {} (q.value (startIdx));
1024  };
1025  }
1026 
1027  template<auto... Ptrs>
1028  QStringList BuildFieldNames () noexcept
1029  {
1030  return { BuildCachedFieldsData<MemberPtrStruct_t<Ptrs>> ().QualifiedFields_.value (FieldIndex<Ptrs> ())... };
1031  }
1032 
1033  enum class SelectBehaviour { Some, One };
1034 
1035  struct OrderNone {};
1036  struct GroupNone {};
1037  struct LimitNone {};
1038  struct OffsetNone {};
1039 
1040  template<size_t RepIdx, size_t TupIdx, typename Tuple, typename NewType>
1041  constexpr decltype (auto) GetReplaceTupleElem (Tuple&& tuple, NewType&& arg) noexcept
1042  {
1043  if constexpr (RepIdx == TupIdx)
1044  return std::forward<NewType> (arg);
1045  else
1046  return std::get<TupIdx> (tuple);
1047  }
1048 
1049  template<size_t RepIdx, typename NewType, typename Tuple, size_t... TupIdxs>
1050  constexpr auto ReplaceTupleElemImpl (Tuple&& tuple, NewType&& arg, std::index_sequence<TupIdxs...>) noexcept
1051  {
1052  return std::tuple
1053  {
1054  GetReplaceTupleElem<RepIdx, TupIdxs> (std::forward<Tuple> (tuple), std::forward<NewType> (arg))...
1055  };
1056  }
1057 
1058  template<size_t RepIdx, typename NewType, typename... TupleArgs>
1059  constexpr auto ReplaceTupleElem (std::tuple<TupleArgs...>&& tuple, NewType&& arg) noexcept
1060  {
1061  return ReplaceTupleElemImpl<RepIdx> (std::move (tuple),
1062  std::forward<NewType> (arg),
1063  std::index_sequence_for<TupleArgs...> {});
1064  }
1065 
1066  template<typename Seq, typename T>
1068  {
1069  constexpr static int Value = 1;
1070  };
1071 
1072  template<typename Seq, typename... Args>
1073  struct DetectShift<Seq, std::tuple<Args...>>
1074  {
1075  constexpr static int Value = (DetectShift<Seq, Args>::Value + ...);
1076  };
1077 
1078  template<typename Seq>
1079  struct DetectShift<Seq, Seq>
1080  {
1081  constexpr static int Value = SeqSize<Seq>;
1082  };
1083 
1084  template<typename... LArgs, typename... RArgs>
1085  auto Combine (std::tuple<LArgs...>&& left, std::tuple<RArgs...>&& right) noexcept
1086  {
1087  return std::tuple_cat (std::move (left), std::move (right));
1088  }
1089 
1090  template<typename... LArgs, typename R>
1091  auto Combine (std::tuple<LArgs...>&& left, const R& right) noexcept
1092  {
1093  return std::tuple_cat (std::move (left), std::tuple { right });
1094  }
1095 
1096  template<typename L, typename... RArgs>
1097  auto Combine (const L& left, std::tuple<RArgs...>&& right) noexcept
1098  {
1099  return std::tuple_cat (std::tuple { left }, std::move (right));
1100  }
1101 
1102  template<typename L, typename R>
1103  auto Combine (const L& left, const R& right) noexcept
1104  {
1105  return std::tuple { left, right };
1106  }
1107 
1109  {
1110  struct All {};
1111  struct First {};
1112  };
1113 
1114  template<typename L, typename R>
1115  constexpr auto CombineBehaviour (L, R) noexcept
1116  {
1117  if constexpr (std::is_same_v<L, ResultBehaviour::First> && std::is_same_v<R, ResultBehaviour::First>)
1118  return ResultBehaviour::First {};
1119  else
1120  return ResultBehaviour::All {};
1121  }
1122 
1123  template<typename ResList>
1124  decltype (auto) HandleResultBehaviour (ResultBehaviour::All, ResList&& list) noexcept
1125  {
1126  return std::forward<ResList> (list);
1127  }
1128 
1129  template<typename ResList>
1130  auto HandleResultBehaviour (ResultBehaviour::First, ResList&& list) noexcept
1131  {
1132  return list.value (0);
1133  }
1134 
1135  template<typename F, typename R>
1137  {
1138  QString Fields_;
1141  };
1142 
1143  template<typename F, typename R>
1145 
1147  {
1148  protected:
1149  const QSqlDatabase DB_;
1150  const QString LimitNone_;
1151 
1152  SelectWrapperCommon (const QSqlDatabase& db, const QString& limitNone)
1153  : DB_ { db }
1154  , LimitNone_ { limitNone }
1155  {
1156  }
1157 
1158  auto RunQuery (const QString& fields, const QString& from,
1159  QString where, std::function<void (QSqlQuery&)>&& binder,
1160  const QString& orderStr,
1161  const QString& groupStr,
1162  const QString& limitOffsetStr) const
1163  {
1164  if (!where.isEmpty ())
1165  where.prepend (" WHERE ");
1166 
1167  const auto& queryStr = "SELECT " + fields +
1168  " FROM " + from +
1169  where +
1170  orderStr +
1171  groupStr +
1172  limitOffsetStr;
1173 
1174  QSqlQuery query { DB_ };
1175  query.prepare (queryStr);
1176  if (binder)
1177  binder (query);
1178 
1179  if (!query.exec ())
1180  {
1181  DBLock::DumpError (query);
1182  throw QueryException ("fetch query execution failed", std::make_shared<QSqlQuery> (query));
1183  }
1184 
1185  return query;
1186  }
1187 
1188  QString HandleLimitOffset (LimitNone, OffsetNone) const noexcept
1189  {
1190  return {};
1191  }
1192 
1193  QString HandleLimitOffset (Limit limit, OffsetNone) const noexcept
1194  {
1195  return " LIMIT " + QString::number (limit.Count);
1196  }
1197 
1198  template<typename L>
1199  QString HandleLimitOffset (L limit, Offset offset) const noexcept
1200  {
1201  QString limitStr;
1202  if constexpr (std::is_same_v<std::decay_t<L>, LimitNone>)
1203  {
1204  Q_UNUSED (limit)
1205  limitStr = LimitNone_;
1206  }
1207  else if constexpr (std::is_integral_v<L>)
1208  limitStr = QString::number (limit);
1209  else
1210  limitStr = QString::number (limit.Count);
1211  return " LIMIT " + limitStr +
1212  " OFFSET " + QString::number (offset.Count);
1213  }
1214  };
1215 
1216  template<typename T, SelectBehaviour SelectBehaviour>
1218  {
1219  const CachedFieldsData Cached_;
1220 
1221  template<typename ParamsTuple>
1222  struct Builder
1223  {
1224  const SelectWrapper& W_;
1225  ParamsTuple Params_;
1226 
1227  template<typename NewTuple>
1228  constexpr auto RepTuple (NewTuple&& tuple) noexcept
1229  {
1230  return Builder<NewTuple> { W_, tuple };
1231  }
1232 
1233  template<typename U>
1234  constexpr auto Select (U&& selector) && noexcept
1235  {
1236  return RepTuple (ReplaceTupleElem<0> (std::move (Params_), std::forward<U> (selector)));
1237  }
1238 
1239  template<typename U>
1240  constexpr auto Where (U&& tree) && noexcept
1241  {
1242  return RepTuple (ReplaceTupleElem<1> (std::move (Params_), std::forward<U> (tree)));
1243  }
1244 
1245  template<typename U>
1246  constexpr auto Order (U&& order) && noexcept
1247  {
1248  return RepTuple (ReplaceTupleElem<2> (std::move (Params_), std::forward<U> (order)));
1249  }
1250 
1251  template<typename U>
1252  constexpr auto Group (U&& group) && noexcept
1253  {
1254  return RepTuple (ReplaceTupleElem<3> (std::move (Params_), std::forward<U> (group)));
1255  }
1256 
1257  template<typename U = Limit>
1258  constexpr auto Limit (U&& limit) && noexcept
1259  {
1260  return RepTuple (ReplaceTupleElem<4> (std::move (Params_), std::forward<U> (limit)));
1261  }
1262 
1263  template<typename U = Offset>
1264  constexpr auto Offset (U&& offset) && noexcept
1265  {
1266  return RepTuple (ReplaceTupleElem<5> (std::move (Params_), std::forward<U> (offset)));
1267  }
1268 
1269  auto operator() () &&
1270  {
1271  return std::apply (W_, Params_);
1272  }
1273 
1274  template<auto... Ptrs>
1275  constexpr auto Group () && noexcept
1276  {
1277  return std::move (*this).Group (GroupBy<Ptrs...> {});
1278  }
1279  };
1280  public:
1281  template<typename ImplFactory>
1282  SelectWrapper (const QSqlDatabase& db, const CachedFieldsData& data, ImplFactory&& factory) noexcept
1283  : SelectWrapperCommon { db, factory.LimitNone }
1284  , Cached_ { data }
1285  {
1286  }
1287 
1288  auto Build () const noexcept
1289  {
1290  std::tuple defParams
1291  {
1292  SelectWhole {},
1294  OrderNone {},
1295  GroupNone {},
1296  LimitNone {},
1297  OffsetNone {}
1298  };
1299  return Builder<decltype (defParams)> { *this, defParams };
1300  }
1301 
1302  auto operator() () const
1303  {
1304  return (*this) (SelectWhole {}, ConstTrueTree_v);
1305  }
1306 
1307  template<typename Single>
1308  auto operator() (Single&& single) const
1309  {
1310  if constexpr (IsExprTree<std::decay_t<Single>> {})
1311  return (*this) (SelectWhole {}, std::forward<Single> (single));
1312  else
1313  return (*this) (std::forward<Single> (single), ConstTrueTree_v);
1314  }
1315 
1316  template<
1317  typename Selector,
1318  ExprType Type, typename L, typename R,
1319  typename Order = OrderNone,
1320  typename Group = GroupNone,
1321  typename Limit = LimitNone,
1322  typename Offset = OffsetNone
1323  >
1324  auto operator() (Selector selector,
1325  const ExprTree<Type, L, R>& tree,
1326  Order order = OrderNone {},
1327  Group group = GroupNone {},
1328  Limit limit = LimitNone {},
1329  Offset offset = OffsetNone {}) const
1330  {
1331  const auto& [where, binder, _] = HandleExprTree<T> (tree);
1332  Q_UNUSED (_);
1333  const auto& [fields, initializer, resultBehaviour] = HandleSelector (std::forward<Selector> (selector));
1334  return HandleResultBehaviour (resultBehaviour,
1335  Select (fields, BuildFromClause (tree),
1336  where, binder,
1337  initializer,
1338  HandleOrder (std::forward<Order> (order)),
1339  HandleGroup (std::forward<Group> (group)),
1340  HandleLimitOffset (std::forward<Limit> (limit), std::forward<Offset> (offset))));
1341  }
1342  private:
1343  template<typename Binder, typename Initializer>
1344  auto Select (const QString& fields, const QString& from,
1345  const QString& where, Binder&& binder,
1346  Initializer&& initializer,
1347  const QString& orderStr,
1348  const QString& groupStr,
1349  const QString& limitOffsetStr) const
1350  {
1351  std::function<void (QSqlQuery&)> binderFunc;
1352  if constexpr (!std::is_same_v<Void, std::decay_t<Binder>>)
1353  binderFunc = binder;
1354  auto query = RunQuery (fields, from, where, std::move (binderFunc), orderStr, groupStr, limitOffsetStr);
1355 
1356  if constexpr (SelectBehaviour == SelectBehaviour::Some)
1357  {
1359  while (query.next ())
1360  result << initializer (query);
1361  return result;
1362  }
1363  else
1364  {
1365  using RetType_t = std::optional<std::result_of_t<Initializer (QSqlQuery)>>;
1366  return query.next () ?
1367  RetType_t { initializer (query) } :
1368  RetType_t {};
1369  }
1370  }
1371 
1372  template<ExprType Type, typename L, typename R>
1373  QString BuildFromClause (const ExprTree<Type, L, R>& tree) const noexcept
1374  {
1375  if constexpr (Type != ExprType::ConstTrue)
1376  {
1377  auto result = Cached_.Table_;
1378  for (const auto& item : tree.template AdditionalTables<T> ())
1379  result += ", " + item;
1380  return result;
1381  }
1382  else
1383  return Cached_.Table_;
1384  }
1385 
1386  auto HandleSelector (SelectWhole) const noexcept
1387  {
1388  return HandleSelectorResult
1389  {
1390  Cached_.QualifiedFields_.join (", "),
1391  [] (const QSqlQuery& q, int startIdx = 0)
1392  {
1393  return InitializeFromQuery<T> (q, SeqIndices<T>, startIdx);
1394  },
1395  ResultBehaviour::All {}
1396  };
1397  }
1398 
1399  template<auto... Ptrs>
1400  auto HandleSelector (MemberPtrs<Ptrs...> ptrs) const noexcept
1401  {
1402  return HandleSelectorResult
1403  {
1404  BuildFieldNames<Ptrs...> ().join (", "),
1405  MakeIndexedQueryHandler (ptrs, std::make_index_sequence<sizeof... (Ptrs)> {}),
1406  ResultBehaviour::All {}
1407  };
1408  }
1409 
1410  auto HandleSelector (AggregateType<AggregateFunction::Count, CountAllPtr>) const noexcept
1411  {
1412  return HandleSelectorResult
1413  {
1414  "count(1)",
1415  [] (const QSqlQuery& q, int startIdx = 0) { return q.value (startIdx).toLongLong (); },
1417  };
1418  }
1419 
1420  template<auto Ptr>
1421  auto HandleSelector (AggregateType<AggregateFunction::Count, Ptr>) const noexcept
1422  {
1423  return HandleSelectorResult
1424  {
1425  "count(" + GetQualifiedFieldNamePtr<Ptr> () + ")",
1426  [] (const QSqlQuery& q, int startIdx = 0) { return q.value (startIdx).toLongLong (); },
1428  };
1429  }
1430 
1431  template<auto Ptr>
1432  auto HandleSelector (AggregateType<AggregateFunction::Min, Ptr>) const noexcept
1433  {
1434  return HandleSelectorResult
1435  {
1436  "min(" + GetQualifiedFieldNamePtr<Ptr> () + ")",
1437  MakeIndexedQueryHandler<Ptr> (),
1439  };
1440  }
1441 
1442  template<auto Ptr>
1443  auto HandleSelector (AggregateType<AggregateFunction::Max, Ptr>) const noexcept
1444  {
1445  return HandleSelectorResult
1446  {
1447  "max(" + GetQualifiedFieldNamePtr<Ptr> () + ")",
1448  MakeIndexedQueryHandler<Ptr> (),
1450  };
1451  }
1452 
1453  template<typename L, typename R>
1454  auto HandleSelector (SelectorUnion<L, R>) const noexcept
1455  {
1456  const auto& lSel = HandleSelector (L {});
1457  const auto& rSel = HandleSelector (R {});
1458 
1459  const auto& lHandler = lSel.Initializer_;
1460  const auto& rHandler = rSel.Initializer_;
1461 
1462  return HandleSelectorResult
1463  {
1464  lSel.Fields_ + ", " + rSel.Fields_,
1465  [lHandler, rHandler] (const QSqlQuery& q, int startIdx = 0)
1466  {
1467  constexpr auto shift = DetectShift<T, decltype (lHandler (q))>::Value;
1468  return Combine (lHandler (q, startIdx), rHandler (q, startIdx + shift));
1469  },
1470  CombineBehaviour (lSel.Behaviour_, rSel.Behaviour_)
1471  };
1472  }
1473 
1474  QString HandleOrder (OrderNone) const noexcept
1475  {
1476  return {};
1477  }
1478 
1479  template<auto... Ptrs>
1480  QList<QString> HandleSuborder (sph::asc<Ptrs...>) const noexcept
1481  {
1482  return { (GetQualifiedFieldNamePtr<Ptrs> () + " ASC")... };
1483  }
1484 
1485  template<auto... Ptrs>
1486  QList<QString> HandleSuborder (sph::desc<Ptrs...>) const noexcept
1487  {
1488  return { (GetQualifiedFieldNamePtr<Ptrs> () + " DESC")... };
1489  }
1490 
1491  template<typename... Suborders>
1492  QString HandleOrder (OrderBy<Suborders...>) const noexcept
1493  {
1494  return " ORDER BY " + QStringList { Concat (QList { HandleSuborder (Suborders {})... }) }.join (", ");
1495  }
1496 
1497  QString HandleGroup (GroupNone) const noexcept
1498  {
1499  return {};
1500  }
1501 
1502  template<auto... Ptrs>
1503  QString HandleGroup (GroupBy<Ptrs...>) const noexcept
1504  {
1505  return " GROUP BY " + QStringList { GetQualifiedFieldNamePtr<Ptrs> ()... }.join (", ");
1506  }
1507  };
1508 
1509  template<typename T>
1511  {
1512  const QSqlDatabase DB_;
1513  const QString Table_;
1514  public:
1515  DeleteByFieldsWrapper (const QSqlDatabase& db, const CachedFieldsData& data) noexcept
1516  : DB_ { db }
1517  , Table_ (data.Table_)
1518  {
1519  }
1520 
1521  template<ExprType Type, typename L, typename R>
1522  void operator() (const ExprTree<Type, L, R>& tree) const noexcept
1523  {
1524  const auto& [where, binder, _] = HandleExprTree<T> (tree);
1525  Q_UNUSED (_);
1526 
1527  const auto& selectAll = "DELETE FROM " + Table_ +
1528  " WHERE " + where;
1529 
1530  QSqlQuery query { DB_ };
1531  query.prepare (selectAll);
1532  binder (query);
1533  query.exec ();
1534  }
1535  };
1536 
1537  template<typename T, bool HasPKey = HasPKey<T>>
1539  {
1540  const QSqlDatabase DB_;
1541  const QString Table_;
1542 
1543  std::function<void (T)> Updater_;
1544  public:
1545  AdaptUpdate (const QSqlDatabase& db, const CachedFieldsData& data) noexcept
1546  : DB_ { db }
1547  , Table_ { data.Table_ }
1548  {
1549  if constexpr (HasPKey)
1550  {
1551  constexpr auto index = FindPKey<T>::result_type::value;
1552 
1553  auto statements = Util::ZipWith<QList> (data.Fields_, data.BoundFields_,
1554  [] (const QString& s1, const QString& s2) { return s1 + " = " + s2; });
1555  auto wherePart = statements.takeAt (index);
1556  const auto& update = "UPDATE " + data.Table_ +
1557  " SET " + statements.join (", ") +
1558  " WHERE " + wherePart;
1559 
1560  const auto updateQuery = std::make_shared<QSqlQuery> (db);
1561  updateQuery->prepare (update);
1562  Updater_ = MakeInserter<T> (data, updateQuery, true);
1563  }
1564  }
1565 
1566  template<bool B = HasPKey>
1567  std::enable_if_t<B> operator() (const T& seq)
1568  {
1569  Updater_ (seq);
1570  }
1571 
1572  template<typename SL, typename SR, ExprType WType, typename WL, typename WR>
1574  {
1575  static_assert (!ExprTree<WType, WL, WR>::template HasAdditionalTables<T> (),
1576  "joins in update statements are not supported by SQL");
1577 
1578  const auto& [setClause, setBinder, setLast] = HandleExprTree<T> (set);
1579  const auto& [whereClause, whereBinder, _] = HandleExprTree<T> (where, setLast);
1580 
1581  const auto& update = "UPDATE " + Table_ +
1582  " SET " + setClause +
1583  " WHERE " + whereClause;
1584 
1585  QSqlQuery query { DB_ };
1586  query.prepare (update);
1587  setBinder (query);
1588  whereBinder (query);
1589  if (!query.exec ())
1590  {
1591  DBLock::DumpError (query);
1592  throw QueryException ("update query execution failed", std::make_shared<QSqlQuery> (query));
1593  }
1594 
1595  return query.numRowsAffected ();
1596  }
1597  };
1598 
1599  template<typename T>
1601 
1602  template<typename T>
1604 
1605  template<typename T>
1607 
1608  template<int... Fields>
1610  {
1611  QString operator() (const CachedFieldsData& data) const noexcept
1612  {
1613  return "UNIQUE (" + QStringList { data.Fields_.value (Fields)... }.join (", ") + ")";
1614  }
1615  };
1616 
1617  template<int... Fields>
1619  {
1620  QString operator() (const CachedFieldsData& data) const noexcept
1621  {
1622  return "PRIMARY KEY (" + QStringList { data.Fields_.value (Fields)... }.join (", ") + ")";
1623  }
1624  };
1625 
1626  template<typename... Args>
1628  {
1629  return { ExtractConstraintFields<Args> {} (data)... };
1630  }
1631 
1632  template<typename ImplFactory, typename T, size_t... Indices>
1633  QList<QString> GetTypes (std::index_sequence<Indices...>) noexcept
1634  {
1635  return { Type2Name<ImplFactory, ValueAtC_t<T, Indices>> {} ()... };
1636  }
1637 
1638  template<typename ImplFactory, typename T>
1639  QString AdaptCreateTable (const CachedFieldsData& data) noexcept
1640  {
1641  const auto& types = GetTypes<ImplFactory, T> (SeqIndices<T>);
1642 
1643  const auto& constraints = GetConstraintsStringList (ConstraintsType<T> {}, data);
1644  const auto& constraintsStr = constraints.isEmpty () ?
1645  QString {} :
1646  (", " + constraints.join (", "));
1647 
1648  const auto& statements = Util::ZipWith<QList> (types, data.Fields_,
1649  [] (const QString& type, const QString& field) { return field + " " + type; });
1650  return "CREATE TABLE " +
1651  data.Table_ +
1652  " (" +
1653  statements.join (", ") +
1654  constraintsStr +
1655  ");";
1656  }
1657  }
1658 
1659  template<auto... Ptrs>
1661  {
1662  return { { detail::BuildCachedFieldsData<MemberPtrStruct_t<Ptrs>> ().Fields_.value (detail::FieldIndex<Ptrs> ())... } };
1663  }
1664 
1665  template<typename Seq>
1667  {
1668  static_assert (detail::HasPKey<Seq>, "Sequence does not have any primary keys");
1669  return { { detail::GetFieldName<Seq, detail::FindPKey<Seq>::result_type::value> () } };
1670  }
1671 
1672  template<typename T>
1673  struct ObjectInfo
1674  {
1678 
1682 
1683  using ObjectType_t = T;
1684  };
1685 
1686  template<typename T, typename ImplFactory = detail::SQLite::ImplFactory>
1687  ObjectInfo<T> Adapt (const QSqlDatabase& db)
1688  {
1689  const auto& cachedData = detail::BuildCachedFieldsData<T> ();
1690 
1691  if (!db.tables ().contains (cachedData.Table_, Qt::CaseInsensitive))
1692  RunTextQuery (db, detail::AdaptCreateTable<ImplFactory, T> (cachedData));
1693 
1694  ImplFactory factory;
1695 
1696  return
1697  {
1698  { db, cachedData, factory },
1699  { db, cachedData },
1700  { db, cachedData },
1701 
1702  { db, cachedData, factory },
1703  { db, cachedData, factory },
1704  { db, cachedData }
1705  };
1706  }
1707 
1708  template<typename T>
1709  using ObjectInfo_ptr = std::shared_ptr<ObjectInfo<T>>;
1710 
1711  template<typename T, typename ImplFactory = SQLiteImplFactory>
1712  ObjectInfo_ptr<T> AdaptPtr (const QSqlDatabase& db)
1713  {
1714  return std::make_shared<ObjectInfo<T>> (Adapt<T, ImplFactory> (db));
1715  }
1716 
1717  namespace detail
1718  {
1719  template<size_t Idx, typename Tuple>
1720  using UnderlyingObject_t = typename std::decay_t<std::tuple_element_t<Idx, Tuple>>::element_type::ObjectType_t;
1721 
1722  template<typename ImplFactory, typename Tuple, size_t... Idxs>
1723  void AdaptPtrs (const QSqlDatabase& db, Tuple& tuple, std::index_sequence<Idxs...>)
1724  {
1725  ((std::get<Idxs> (tuple) = AdaptPtr<UnderlyingObject_t<Idxs, Tuple>, ImplFactory> (db)), ...);
1726  }
1727  }
1728 
1729  template<typename ImplFactory, typename Tuple>
1730  void AdaptPtrs (const QSqlDatabase& db, Tuple& tuple)
1731  {
1732  detail::AdaptPtrs<ImplFactory> (db, tuple, std::make_index_sequence<std::tuple_size_v<Tuple>> {});
1733  }
1734 }
1735 }
1736 }
typename std::decay_t< std::tuple_element_t< Idx, Tuple > >::element_type::ObjectType_t UnderlyingObject_t
Definition: oral.h:1720
decltype(std::declval< UnwrapIndirect_t< typename L::template ValueType_t< Seq > >>()==std::declval< UnwrapIndirect_t< typename R::template ValueType_t< Seq > >>()) ComparableDetector
Definition: oral.h:598
constexpr bool IsRelational(ExprType type) noexcept
Definition: oral.h:567
constexpr detail::AggregateType< detail::AggregateFunction::Count, Ptr > count
Definition: oral.h:969
detail::DeleteByFieldsWrapper< T > DeleteBy
Definition: oral.h:1681
auto Stlize(Assoc &&assoc)
Converts an Qt&#39;s associative sequence assoc to an STL-like iteratable range.
Definition: qtutil.h:115
constexpr size_t FieldIndex() noexcept
Definition: oral.h:162
QString GetFieldName() noexcept
Definition: oral.h:117
constexpr detail::OrderBy< Orders... > OrderBy
Definition: oral.h:979
typename boost::fusion::result_of::value_at_c< Seq, Idx >::type ValueAtC_t
Definition: oral.h:343
typename T::Constraints ConstraintsDetector
Definition: oral.h:1600
constexpr auto HasAutogenPKey() noexcept
Definition: oral.h:374
AdaptDelete(const QSqlDatabase &, const CachedFieldsData &, std::enable_if_t<!B > *=nullptr) noexcept
Definition: oral.h:484
std::enable_if_t< B > operator()(const T &seq)
Definition: oral.h:1567
decltype(T::TypeName) TypeNameDetector
Definition: oral.h:197
constexpr bool TypeNameCustomized
Definition: oral.h:200
constexpr auto SeqSize
Definition: oral.h:123
constexpr CountAll * CountAllPtr
Definition: oral.h:917
uint64_t Count
Definition: oral.h:986
QString MorphFieldName(QString str) noexcept
Definition: oral.h:104
QSet< QString > AdditionalTables() const noexcept
Definition: oral.h:674
Container< T > Concat(const Container< Container< T >> &containers)
Definition: prelude.h:186
Open "Replace" dialog.
QString operator()() const noexcept
Definition: oral.h:212
constexpr detail::AggregateType< detail::AggregateFunction::Max, Ptr > max
Definition: oral.h:975
AssignList(const L &l, const R &r) noexcept
Definition: oral.h:630
QStringList operator()() const noexcept
Definition: oral.h:131
QString HandleLimitOffset(LimitNone, OffsetNone) const noexcept
Definition: oral.h:1188
constexpr auto AreComparableTypes
Definition: oral.h:601
constexpr detail::GroupBy< Ptrs... > GroupBy
Definition: oral.h:982
boost::mpl::int_< FindPKey< Seq >::result_type::value > FindPKeyDetector
Definition: oral.h:368
typename std::conditional_t< IsPKey< ValueAt_t< Seq, MemberIdx > >::value, Lazy< MemberIdx >, Lazy< FindPKey< Seq, typename boost::mpl::next< MemberIdx >::type > > >::type result_type
Definition: oral.h:364
SelectWrapperCommon(const QSqlDatabase &db, const QString &limitNone)
Definition: oral.h:1152
ExprTreeHandler(const QString &sql, F &&binder, int lastId) noexcept
Definition: oral.h:871
static UTIL_DB_API void DumpError(const QSqlError &error)
Dumps the error to the qWarning() stream.
Definition: dblock.cpp:84
CachedFieldsData BuildCachedFieldsData(const QString &table) noexcept
Definition: oral.h:383
SelectorUnion< L, R > operator+(L, R) noexcept
Definition: oral.h:946
T operator()(const QVariant &var) const noexcept
Definition: oral.h:289
AdaptInsert(const QSqlDatabase &db, CachedFieldsData data, ImplFactory &&factory) noexcept
Definition: oral.h:410
HandleSelectorResult(QString, F, R) -> HandleSelectorResult< F, R >
typename detail::IsDetected< Type, void, Op, Args... >::type IsDetected_t
Definition: detector.h:59
QVariant operator()(const T &t) const noexcept
Definition: oral.h:269
auto Build() const noexcept
Definition: oral.h:1288
auto HandleExprTree(const ExprTree< ExprType::ConstTrue > &, int lastId=0) noexcept
Definition: oral.h:880
STL namespace.
static constexpr auto Index() noexcept
Definition: oral.h:155
auto operator,(const AssignList< OL, OR > &tail) noexcept
Definition: oral.h:646
MemberTypeType_t< decltype(Ptr)> MemberPtrType_t
Definition: typegetter.h:101
~QueryException() noexcept=default
constexpr bool IsDetected_v
Definition: detector.h:56
static constexpr auto Ptr() noexcept
Definition: oral.h:149
T InitializeFromQuery(const QSqlQuery &q, std::index_sequence< Indices... >, int startIdx) noexcept
Definition: oral.h:499
static constexpr bool HasAdditionalTables() noexcept
Definition: oral.h:714
QString ToSql(ToSqlState< ObjT > &state) const noexcept
Definition: oral.h:700
auto operator<(const L &left, const R &right) noexcept
Definition: oral.h:800
constexpr bool BaseTypeCustomized
Definition: oral.h:206
DeleteByFieldsWrapper(const QSqlDatabase &db, const CachedFieldsData &data) noexcept
Definition: oral.h:1515
QueryException(const std::string &str, const QSqlQuery_ptr &q)
Definition: oral.h:79
std::shared_ptr< QSqlQuery > QSqlQuery_ptr
Definition: oral.h:73
decltype(auto) HandleResultBehaviour(ResultBehaviour::All, ResList &&list) noexcept
Definition: oral.h:1124
auto operator!=(const L &left, const R &right) noexcept
Definition: oral.h:818
auto RunQuery(const QString &fields, const QString &from, QString where, std::function< void(QSqlQuery &)> &&binder, const QString &orderStr, const QString &groupStr, const QString &limitOffsetStr) const
Definition: oral.h:1158
auto MakeExprTree(const L &left, const R &right) noexcept
Definition: oral.h:779
QList< QString > GetTypes(std::index_sequence< Indices... >) noexcept
Definition: oral.h:1633
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition: oral.h:955
QString ToSql(ToSqlState< T > &state) const noexcept
Definition: oral.h:637
constexpr auto ReplaceTupleElemImpl(Tuple &&tuple, NewType &&arg, std::index_sequence< TupIdxs... >) noexcept
Definition: oral.h:1050
void operator()(const ExprTree< Type, L, R > &tree) const noexcept
Definition: oral.h:1522
void AdaptPtrs(const QSqlDatabase &db, Tuple &tuple, std::index_sequence< Idxs... >)
Definition: oral.h:1723
constexpr auto HasPKey
Definition: oral.h:371
auto operator &&(const L &left, const R &right) noexcept
Definition: oral.h:853
Type
Describes the various types of XDG .desktop files.
Definition: itemtypes.h:48
void Unreachable()
Definition: unreachable.h:36
auto First(F &&f)
Definition: prelude.h:260
constexpr auto SeqIndices
Definition: oral.h:126
QString AdaptCreateTable(const CachedFieldsData &data) noexcept
Definition: oral.h:1639
void AdaptPtrs(const QSqlDatabase &db, Tuple &tuple)
Definition: oral.h:1730
constexpr bool EitherIsExprTree() noexcept
Definition: oral.h:787
detail::AdaptUpdate< T > Update
Definition: oral.h:1676
AdaptDelete(const QSqlDatabase &db, const CachedFieldsData &data, std::enable_if_t< B > *=nullptr) noexcept
Definition: oral.h:463
QStringList BuildFieldNames() noexcept
Definition: oral.h:1028
auto MakeInserter(const CachedFieldsData &data, const QSqlQuery_ptr &insertQuery, bool bindPrimaryKey) noexcept
Definition: oral.h:321
QString ToSql(ToSqlState< T > &state) const noexcept
Definition: oral.h:665
auto Map(Container &&c, F f)
Definition: prelude.h:164
Fields_t Fields_
static constexpr bool HasAdditionalTables() noexcept
Definition: oral.h:680
auto operator==(const L &left, const R &right) noexcept
Definition: oral.h:812
std::unique_ptr< IInsertQueryBuilder > IInsertQueryBuilder_ptr
Definition: impldefs.h:48
decltype(new T { std::declval< Args >()... }) AggregateDetector_t
Definition: oral.h:496
QString GetQualifiedFieldNamePtr() noexcept
Definition: oral.h:190
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
Definition: oral.h:1712
static struct LC::Util::oral::InsertAction::DefaultTag Default
A somewhat "strong" typedef.
Definition: newtype.h:53
typename AsTypelist< T >::Result_t AsTypelist_t
Definition: typelist.h:161
QSet< QString > AdditionalTables() const noexcept
Definition: oral.h:708
auto operator>(const L &left, const R &right) noexcept
Definition: oral.h:806
detail::AdaptInsert< T > Insert
Definition: oral.h:1675
std::conditional_t< std::is_same_v< detail::RetTypeRaw_t< F >, detail::ReturnsVoid >, void, detail::RetTypeRaw_t< F > > RetType_t
Definition: typegetter.h:77
typename boost::fusion::result_of::value_at< Seq, Idx >::type ValueAt_t
Definition: oral.h:346
const QSqlQuery_ptr & GetQueryPtr() const
Definition: oral.h:87
static constexpr int Value
Definition: oral.h:1069
QStringList GetConstraintsStringList(Constraints< Args... >, const CachedFieldsData &data) noexcept
Definition: oral.h:1627
std::enable_if_t< B > operator()(const Seq &seq)
Definition: oral.h:489
const QSqlQuery & GetQuery() const
Definition: oral.h:92
constexpr auto ReplaceTupleElem(std::tuple< TupleArgs... > &&tuple, NewType &&arg) noexcept
Definition: oral.h:1059
detail::SelectWrapper< T, detail::SelectBehaviour::Some > Select
Definition: oral.h:1679
auto Combine(std::tuple< LArgs... > &&left, std::tuple< RArgs... > &&right) noexcept
Definition: oral.h:1085
constexpr detail::InfixBinary< detail::ExprType::Like > like
Definition: oral.h:829
SelectWrapper(const QSqlDatabase &db, const CachedFieldsData &data, ImplFactory &&factory) noexcept
Definition: oral.h:1282
ExprTree(const L &l, const R &r) noexcept
Definition: oral.h:658
constexpr detail::MemberPtrs< Ptrs... > fields
Definition: oral.h:958
typename T::BaseType BaseTypeDetector
Definition: oral.h:203
QSqlQuery RunTextQuery(const QSqlDatabase &db, const QString &text)
Runs the given query text on the given db.
Definition: util.cpp:40
constexpr detail::AggregateType< detail::AggregateFunction::Min, Ptr > min
Definition: oral.h:972
constexpr auto CombineBehaviour(L, R) noexcept
Definition: oral.h:1115
auto operator()(Seq &t, InsertAction action=InsertAction::Default) const
Definition: oral.h:416
constexpr auto AsLeafData(const T &node) noexcept
Definition: oral.h:721
AdaptUpdate(const QSqlDatabase &db, const CachedFieldsData &data) noexcept
Definition: oral.h:1545
typename std::conditional_t< IsIndirect< T > {}, T, WrapDirect< T > >::value_type UnwrapIndirect_t
Definition: oral.h:594
MemberTypeStruct_t< decltype(Ptr)> MemberPtrStruct_t
Definition: typegetter.h:104
Util::IsDetected_t< Constraints<>, ConstraintsDetector, T > ConstraintsType
Definition: oral.h:1603
QString HandleLimitOffset(L limit, Offset offset) const noexcept
Definition: oral.h:1199
ObjectInfo< T > Adapt(const QSqlDatabase &db)
Definition: oral.h:1687
decltype(auto) constexpr GetReplaceTupleElem(Tuple &&tuple, NewType &&arg) noexcept
Definition: oral.h:1041
decltype(std::declval< U >().FieldNameMorpher(QString {})) MorpherDetector
Definition: oral.h:101
detail::SelectWrapper< T, detail::SelectBehaviour::One > SelectOne
Definition: oral.h:1680
constexpr detail::SelectWhole all
Definition: oral.h:960
QString HandleLimitOffset(Limit limit, OffsetNone) const noexcept
Definition: oral.h:1193
auto MakeIndexedQueryHandler(MemberPtrs< Ptrs... >, std::index_sequence< Idxs... >) noexcept
Definition: oral.h:1007
Typelist< Args... > Constraints
Definition: oraltypes.h:164
std::enable_if_t< EitherIsExprTree< L, R >()> EnableRelOp_t
Definition: oral.h:797
auto operator||(const L &left, const R &right) noexcept
Definition: oral.h:859
Definition: constants.h:35
detail::AdaptDelete< T > Delete
Definition: oral.h:1677
auto operator|(const L &left, InfixBinary< Op >) noexcept
Definition: oral.h:841
QString TypeToSql(ExprType type) noexcept
Definition: oral.h:535
uint64_t Count
Definition: oral.h:996
Offset(uint64_t count) noexcept
Definition: oral.h:998
Limit(uint64_t count) noexcept
Definition: oral.h:988
QVariant ToVariantF(const T &t) noexcept
Definition: oral.h:315
std::shared_ptr< ObjectInfo< T > > ObjectInfo_ptr
Definition: oral.h:1709
QString GetFieldNamePtr() noexcept
Definition: oral.h:183
constexpr auto ConstTrueTree_v
Definition: oral.h:776
std::function< void(Seq)> Deleter_
Definition: oral.h:460