LeechCraft  0.6.70-10870-g558588d6ec
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 <boost/fusion/include/for_each.hpp>
36 #include <boost/fusion/include/fold.hpp>
37 #include <boost/fusion/include/filter_if.hpp>
38 #include <boost/fusion/container/vector.hpp>
39 #include <boost/fusion/include/vector.hpp>
40 #include <boost/fusion/include/transform.hpp>
41 #include <boost/fusion/include/zip.hpp>
42 #include <boost/fusion/container/generation/make_vector.hpp>
43 #include <boost/variant/variant.hpp>
44 #include <boost/optional.hpp>
45 #include <QStringList>
46 #include <QDateTime>
47 #include <QPair>
48 #include <QSqlQuery>
49 #include <QSqlRecord>
50 #include <QVariant>
51 #include <QDateTime>
52 #include <QtDebug>
53 #include <util/sll/qtutil.h>
54 #include <util/sll/prelude.h>
55 #include <util/sll/typelist.h>
56 #include <util/sll/typelevel.h>
57 #include <util/sll/typegetter.h>
58 #include <util/sll/detector.h>
59 #include <util/sll/unreachable.h>
60 #include <util/sll/void.h>
61 #include <util/db/dblock.h>
62 #include <util/db/util.h>
63 #include "oraltypes.h"
64 
65 using QSqlQuery_ptr = std::shared_ptr<QSqlQuery>;
66 
67 namespace LeechCraft
68 {
69 namespace Util
70 {
71 namespace oral
72 {
73  class QueryException : public std::runtime_error
74  {
75  const QSqlQuery_ptr Query_;
76  public:
77  QueryException (const std::string& str, const QSqlQuery_ptr& q)
78  : std::runtime_error (str)
79  , Query_ (q)
80  {
81  }
82 
83  virtual ~QueryException () throw ()
84  {
85  }
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)
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>
118  {
119  static QString value ()
120  {
121  return MorphFieldName<Seq> (boost::fusion::extension::struct_member_name<Seq, Idx>::call ());
122  }
123  };
124 
125  template<typename S>
126  constexpr auto SeqSize = boost::fusion::result_of::size<S>::type::value;
127 
128  template<typename S>
129  constexpr auto SeqIndices = std::make_index_sequence<SeqSize<S>> {};
130 
131  template<typename S>
133  {
134  QStringList operator() () const
135  {
136  return Run (SeqIndices<S>);
137  }
138  private:
139  template<size_t... Vals>
140  QStringList Run (std::index_sequence<Vals...>) const
141  {
142  return { GetFieldName<S, Vals>::value ()... };
143  }
144  };
145 
146  template<typename Seq, int Idx>
148  {
149  static QString value () { return ':' + Seq::ClassName () + "_" + GetFieldName<Seq, Idx>::value (); }
150  };
151 
152  template<typename S>
153  struct AddressOf
154  {
155  inline static S Obj_ {};
156 
157  template<auto P>
158  static constexpr auto Ptr ()
159  {
160  return &(Obj_.*P);
161  }
162 
163  template<int Idx>
164  static constexpr auto Index ()
165  {
166  return &boost::fusion::at_c<Idx> (Obj_);
167  }
168  };
169 
170  template<auto Ptr, size_t Idx = 0>
171  constexpr size_t FieldIndex ()
172  {
173  using S = MemberPtrStruct_t<Ptr>;
174 
175  if constexpr (Idx == SeqSize<S>)
176  throw std::runtime_error { "wut, no such field?" };
177  else
178  {
179  constexpr auto direct = AddressOf<S>::template Ptr<Ptr> ();
180  constexpr auto indexed = AddressOf<S>::template Index<Idx> ();
181  if constexpr (std::is_same_v<decltype (direct), decltype (indexed)>)
182  {
183  if (indexed == direct)
184  return Idx;
185  }
186 
187  return FieldIndex<Ptr, Idx + 1> ();
188  }
189  }
190  }
191 
192  template<typename T, typename = void>
193  struct Type2Name
194  {
195  QString operator() () const
196  {
197  if constexpr (HasType<T> (Typelist<int, qulonglong, bool> {}) || std::is_enum_v<T>)
198  return "INTEGER";
199  else if constexpr (std::is_same_v<T, double>)
200  return "REAL";
201  else if constexpr (std::is_same_v<T, QString> || std::is_same_v<T, QDateTime>)
202  return "TEXT";
203  else if constexpr (std::is_same_v<T, QByteArray>)
204  return "BLOB";
205  else
206  static_assert (std::is_same_v<T, struct Dummy>, "Unsupported type");
207  }
208  };
209 
210  template<typename T>
211  struct Type2Name<Unique<T>>
212  {
213  QString operator() () const { return Type2Name<T> () () + " UNIQUE"; }
214  };
215 
216  template<typename T>
217  struct Type2Name<NotNull<T>>
218  {
219  QString operator() () const { return Type2Name<T> () () + " NOT NULL"; }
220  };
221 
222  template<typename T, typename... Tags>
223  struct Type2Name<PKey<T, Tags...>>
224  {
225  QString operator() () const { return Type2Name<T> () () + " PRIMARY KEY"; }
226  };
227 
228  template<typename... Tags>
229  struct Type2Name<PKey<int, Tags...>>
230  {
231  QString operator() () const { return Type2Name<int> () () + " PRIMARY KEY AUTOINCREMENT"; }
232  };
233 
234  template<auto Ptr>
235  struct Type2Name<References<Ptr>>
236  {
237  QString operator() () const
238  {
239  using Seq = MemberPtrStruct_t<Ptr>;
240  constexpr auto idx = detail::FieldIndex<Ptr> ();
241  return Type2Name<ReferencesValue_t<Ptr>> () () +
242  " REFERENCES " + Seq::ClassName () + " (" + detail::GetFieldName<Seq, idx>::value () + ") ON DELETE CASCADE";
243  }
244  };
245 
246  template<typename T, typename = void>
247  struct ToVariant
248  {
249  QVariant operator() (const T& t) const
250  {
251  if constexpr (std::is_same_v<T, QDateTime>)
252  return t.toString (Qt::ISODate);
253  else if constexpr (std::is_enum_v<T>)
254  return static_cast<qint64> (t);
255  else if constexpr (IsIndirect<T> {})
256  return ToVariant<typename T::value_type> {} (t);
257  else
258  return t;
259  }
260  };
261 
262  template<typename T, typename = void>
263  struct FromVariant
264  {
265  T operator() (const QVariant& var) const
266  {
267  if constexpr (std::is_same_v<T, QDateTime>)
268  return QDateTime::fromString (var.toString (), Qt::ISODate);
269  else if constexpr (std::is_enum_v<T>)
270  return static_cast<T> (var.value<qint64> ());
271  else if constexpr (IsIndirect<T> {})
272  return FromVariant<typename T::value_type> {} (var);
273  else
274  return var.value<T> ();
275  }
276  };
277 
278  enum class InsertAction
279  {
280  Default,
281  Ignore,
282  Replace
283  };
284 
285  constexpr size_t InsertActionCount = 3;
286 
287  namespace detail
288  {
289  template<typename T>
290  QVariant ToVariantF (const T& t)
291  {
292  return ToVariant<T> {} (t);
293  }
294 
296  {
297  QString Table_;
298 
299  QStringList Fields_;
300  QStringList QualifiedFields_;
301  QStringList BoundFields_;
302  };
303 
304  template<typename T>
306  {
307  const auto& fields = detail::GetFieldsNames<T> {} ();
308  const auto& qualified = Util::Map (fields, [&table] (const QString& field) { return table + "." + field; });
309  const auto& boundFields = Util::Map (fields, [] (const QString& str) { return ':' + str; });
310 
311  return { table, fields, qualified, boundFields };
312  }
313 
314  template<typename T>
316  {
317  static CachedFieldsData result = BuildCachedFieldsData<T> (T::ClassName ());
318  return result;
319  }
320 
321  template<typename T>
322  auto MakeInserter (const CachedFieldsData& data, const QSqlQuery_ptr& insertQuery, bool bindPrimaryKey)
323  {
324  return [data, insertQuery, bindPrimaryKey] (const T& t)
325  {
326  boost::fusion::fold (t, data.BoundFields_.begin (),
327  [&] (auto pos, const auto& elem)
328  {
329  using Elem = std::decay_t<decltype (elem)>;
330  if (bindPrimaryKey || !IsPKey<Elem>::value)
331  insertQuery->bindValue (*pos++, ToVariantF (elem));
332  return pos;
333  });
334 
335  if (!insertQuery->exec ())
336  {
337  DBLock::DumpError (*insertQuery);
338  throw QueryException ("insert query execution failed", insertQuery);
339  }
340  };
341  }
342 
343  template<typename Seq, int Idx>
344  using ValueAtC_t = typename boost::fusion::result_of::value_at_c<Seq, Idx>::type;
345 
346  template<typename Seq, typename Idx>
347  using ValueAt_t = typename boost::fusion::result_of::value_at<Seq, Idx>::type;
348 
349  template<typename Seq, typename MemberIdx = boost::mpl::int_<0>>
350  struct FindPKey
351  {
352  static_assert ((boost::fusion::result_of::size<Seq>::value) != (MemberIdx::value),
353  "Primary key not found");
354 
355  template<typename T>
356  struct Lazy
357  {
358  using type = T;
359  };
360 
361  using result_type = typename std::conditional_t<
365  >::type;
366  };
367 
368  template<typename Seq>
369  using FindPKeyDetector = boost::mpl::int_<FindPKey<Seq>::result_type::value>;
370 
371  template<typename Seq>
372  constexpr auto HasPKey = IsDetected_v<FindPKeyDetector, Seq>;
373 
374  template<typename Seq>
375  constexpr auto HasAutogenPKey ()
376  {
377  if constexpr (HasPKey<Seq>)
378  return !HasType<NoAutogen> (AsTypelist_t<ValueAtC_t<Seq, FindPKey<Seq>::result_type::value>> {});
379  else
380  return false;
381  }
382 
383  inline QString GetInsertPrefix (InsertAction action)
384  {
385  switch (action)
386  {
388  return "INSERT";
390  return "INSERT OR IGNORE";
392  return "INSERT OR REPLACE";
393  }
394 
396  }
397 
398  template<typename Seq>
399  struct AdaptInsert
400  {
401  const QSqlDatabase DB_;
403  const QString InsertSuffix_;
404 
405  constexpr static bool HasAutogen_ = HasAutogenPKey<Seq> ();
406 
407  mutable std::array<QSqlQuery_ptr, InsertActionCount> Queries_;
408  public:
409  AdaptInsert (const QSqlDatabase& db, CachedFieldsData data)
410  : DB_ { db }
411  , Data_
412  {
413  [data] () mutable
414  {
415  if constexpr (HasAutogen_)
416  {
417  constexpr auto index = FindPKey<Seq>::result_type::value;
418  data.Fields_.removeAt (index);
419  data.BoundFields_.removeAt (index);
420  }
421  return data;
422  } ()
423  }
424  , InsertSuffix_ { " INTO " + Data_.Table_ +
425  " (" + Data_.Fields_.join (", ") + ") VALUES (" +
426  Data_.BoundFields_.join (", ") + ");" }
427  {
428  }
429 
430  auto operator() (Seq& t, InsertAction action = InsertAction::Default) const
431  {
432  return Run<true> (t, action);
433  }
434 
435  auto operator() (const Seq& t, InsertAction action = InsertAction::Default) const
436  {
437  return Run<false> (t, action);
438  }
439  private:
440  template<bool UpdatePKey, typename Val>
441  auto Run (Val&& t, InsertAction action) const
442  {
443  const auto query = GetQuery (action);
444 
445  MakeInserter<Seq> (Data_, query, !HasAutogen_) (t);
446 
447  if constexpr (HasAutogen_)
448  {
449  constexpr auto index = FindPKey<Seq>::result_type::value;
450 
451  const auto& lastId = FromVariant<ValueAtC_t<Seq, index>> {} (query->lastInsertId ());
452  if constexpr (UpdatePKey)
453  boost::fusion::at_c<index> (t) = lastId;
454  else
455  return lastId;
456  }
457  }
458 
459  auto GetQuery (InsertAction action) const
460  {
461  auto& query = Queries_ [static_cast<size_t> (action)];
462  if (!query)
463  {
464  query = std::make_shared<QSqlQuery> (DB_);
465  query->prepare (GetInsertPrefix (action) + InsertSuffix_);
466  }
467  return query;
468  }
469  };
470 
471  template<typename Seq, bool HasPKey = HasPKey<Seq>>
472  struct AdaptDelete
473  {
474  std::function<void (Seq)> Deleter_;
475  public:
476  template<bool B = HasPKey>
477  AdaptDelete (const QSqlDatabase& db, const CachedFieldsData& data, std::enable_if_t<B>* = nullptr)
478  {
479  const auto index = FindPKey<Seq>::result_type::value;
480 
481  const auto& boundName = data.BoundFields_.at (index);
482  const auto& del = "DELETE FROM " + data.Table_ +
483  " WHERE " + data.Fields_.at (index) + " = " + boundName + ";";
484 
485  const auto deleteQuery = std::make_shared<QSqlQuery> (db);
486  deleteQuery->prepare (del);
487 
488  Deleter_ = [deleteQuery, boundName] (const Seq& t)
489  {
490  constexpr auto index = FindPKey<Seq>::result_type::value;
491  deleteQuery->bindValue (boundName, ToVariantF (boost::fusion::at_c<index> (t)));
492  if (!deleteQuery->exec ())
493  throw QueryException ("delete query execution failed", deleteQuery);
494  };
495  }
496 
497  template<bool B = HasPKey>
498  AdaptDelete (const QSqlDatabase&, const CachedFieldsData&, std::enable_if_t<!B>* = nullptr)
499  {
500  }
501 
502  template<bool B = HasPKey>
503  std::enable_if_t<B> operator() (const Seq& seq)
504  {
505  Deleter_ (seq);
506  }
507  };
508 
509  template<typename T, typename... Args>
510  using AggregateDetector_t = decltype (new T { std::declval<Args> ()... });
511 
512  template<typename T, size_t... Indices>
513  T InitializeFromQuery (const QSqlQuery& q, std::index_sequence<Indices...>)
514  {
516  return T { FromVariant<ValueAtC_t<T, Indices>> {} (q.value (Indices))... };
517  else
518  {
519  T t;
520  const auto dummy = std::initializer_list<int>
521  {
522  (static_cast<void> (boost::fusion::at_c<Indices> (t) = FromVariant<ValueAtC_t<T, Indices>> {} (q.value (Indices))), 0)...
523  };
524  Q_UNUSED (dummy);
525  return t;
526  }
527  }
528 
529  template<int HeadT, int... TailT>
531  {
532  static const int Head = HeadT;
533  using Tail_t = FieldsUnpacker<TailT...>;
534  };
535 
536  template<int HeadT>
537  struct FieldsUnpacker<HeadT>
538  {
539  static const int Head = HeadT;
540  using Tail_t = std::false_type;
541  };
542 
543  template<typename FieldsUnpacker, typename HeadArg, typename... TailArgs>
544  struct ValueBinder
545  {
548 
549  void operator() (const HeadArg& arg, const TailArgs&... tail) const
550  {
551  Query_->bindValue (BoundFields_.at (FieldsUnpacker::Head), arg);
552 
553  ValueBinder<typename FieldsUnpacker::Tail_t, TailArgs...> { Query_, BoundFields_ } (tail...);
554  }
555  };
556 
557  template<typename FieldsUnpacker, typename HeadArg>
558  struct ValueBinder<FieldsUnpacker, HeadArg>
559  {
562 
563  void operator() (const HeadArg& arg) const
564  {
565  Query_->bindValue (BoundFields_.at (FieldsUnpacker::Head), arg);
566  }
567  };
568 
569  enum class ExprType
570  {
571  ConstTrue,
572 
574  LeafData,
575 
576  Greater,
577  Less,
578  Equal,
579  Geq,
580  Leq,
581  Neq,
582 
583  And,
584  Or
585  };
586 
587  inline QString TypeToSql (ExprType type)
588  {
589  switch (type)
590  {
591  case ExprType::Greater:
592  return ">";
593  case ExprType::Less:
594  return "<";
595  case ExprType::Equal:
596  return "=";
597  case ExprType::Geq:
598  return ">=";
599  case ExprType::Leq:
600  return "<=";
601  case ExprType::Neq:
602  return "!=";
603  case ExprType::And:
604  return "AND";
605  case ExprType::Or:
606  return "OR";
607 
609  case ExprType::LeafData:
610  case ExprType::ConstTrue:
611  return "invalid type";
612  }
613 
615  }
616 
617  constexpr bool IsRelational (ExprType type)
618  {
619  return type == ExprType::Greater ||
620  type == ExprType::Less ||
621  type == ExprType::Equal ||
622  type == ExprType::Geq ||
623  type == ExprType::Leq ||
624  type == ExprType::Neq;
625  }
626 
627  template<typename T>
628  struct ToSqlState
629  {
630  int LastID_;
631  QVariantMap BoundMembers_;
632  };
633 
634  template<typename T>
635  struct WrapDirect
636  {
637  using value_type = T;
638  };
639 
640  template<typename T>
641  using UnwrapIndirect_t = typename std::conditional_t<IsIndirect<T> {},
642  T,
643  WrapDirect<T>>::value_type;
644 
645  template<typename Seq, typename L, typename R>
646  using ComparableDetector = decltype (std::declval<UnwrapIndirect_t<typename L::template ValueType_t<Seq>>> () ==
647  std::declval<UnwrapIndirect_t<typename R::template ValueType_t<Seq>>> ());
648 
649  template<typename Seq, typename L, typename R>
650  constexpr auto AreComparableTypes = IsDetected_v<ComparableDetector, Seq, L, R> || IsDetected_v<ComparableDetector, Seq, R, L>;
651 
652  template<typename Seq, typename L, typename R, typename = void>
653  struct RelationalTypesCheckerBase : std::false_type {};
654 
655  template<typename Seq, typename L, typename R>
656  struct RelationalTypesCheckerBase<Seq, L, R, std::enable_if_t<AreComparableTypes<Seq, L, R>>> : std::true_type {};
657 
658  template<ExprType Type, typename Seq, typename L, typename R, typename = void>
659  struct RelationalTypesChecker : std::true_type {};
660 
661  template<ExprType Type, typename Seq, typename L, typename R>
662  struct RelationalTypesChecker<Type, Seq, L, R, std::enable_if_t<IsRelational (Type)>> : RelationalTypesCheckerBase<Seq, L, R> {};
663 
664  template<ExprType Type, typename L = void, typename R = void>
665  class ExprTree;
666 
667  template<typename T>
668  struct IsExprTree : std::false_type {};
669 
670  template<ExprType Type, typename L, typename R>
671  struct IsExprTree<ExprTree<Type, L, R>> : std::true_type {};
672 
673  template<typename L, typename R>
675  {
676  L Left_;
677  R Right_;
678  public:
679  AssignList (const L& l, const R& r)
680  : Left_ { l }
681  , Right_ { r }
682  {
683  }
684 
685  template<typename T>
686  QString ToSql (ToSqlState<T>& state) const
687  {
688  if constexpr (IsExprTree<L> {})
689  return Left_.GetFieldName () + " = " + Right_.ToSql (state);
690  else
691  return Left_.ToSql (state) + ", " + Right_.ToSql (state);
692  }
693 
694  template<typename OL, typename OR>
695  auto operator, (const AssignList<OL, OR>& tail)
696  {
697  return AssignList<AssignList<L, R>, AssignList<OL, OR>> { *this, tail };
698  }
699  };
700 
701  template<ExprType Type, typename L, typename R>
702  class ExprTree
703  {
704  L Left_;
705  R Right_;
706  public:
707  ExprTree (const L& l, const R& r)
708  : Left_ (l)
709  , Right_ (r)
710  {
711  }
712 
713  template<typename T>
714  QString ToSql (ToSqlState<T>& state) const
715  {
717  "Incompatible types passed to a relational operator.");
718 
719  return Left_.ToSql (state) + " " + TypeToSql (Type) + " " + Right_.ToSql (state);
720  }
721 
722  template<typename T>
723  QSet<QString> AdditionalTables () const
724  {
725  return Left_.template AdditionalTables<T> () + Right_.template AdditionalTables<T> ();
726  }
727  };
728 
729  template<int Idx>
730  class ExprTree<ExprType::LeafStaticPlaceholder, boost::mpl::int_<Idx>, void>
731  {
732  public:
733  template<typename T>
735 
736  template<typename T>
737  QString ToSql (ToSqlState<T>&) const
738  {
739  static_assert (Idx < boost::fusion::result_of::size<T>::type::value, "Index out of bounds.");
741  }
742 
743  template<typename>
744  QSet<QString> AdditionalTables () const
745  {
746  return {};
747  }
748  };
749 
750  template<auto... Ptr>
751  struct MemberPtrs {};
752 
753  template<auto Ptr>
755  {
756  public:
757  template<typename>
759 
760  template<typename T>
761  QString ToSql (ToSqlState<T>&) const
762  {
763  return MemberPtrStruct_t<Ptr>::ClassName () + "." + GetFieldName ();
764  }
765 
766  QString GetFieldName () const
767  {
768  using Seq = MemberPtrStruct_t<Ptr>;
769  constexpr auto idx = FieldIndex<Ptr> ();
771  }
772 
773  template<typename T>
774  QSet<QString> AdditionalTables () const
775  {
776  using Seq = MemberPtrStruct_t<Ptr>;
777  if constexpr (std::is_same_v<Seq, T>)
778  return {};
779  else
780  return { Seq::ClassName () };
781  }
782 
783  template<typename R>
784  auto operator= (const R&) const;
785  };
786 
787  template<typename T>
788  class ExprTree<ExprType::LeafData, T, void>
789  {
790  T Data_;
791  public:
792  template<typename>
793  using ValueType_t = T;
794 
795  ExprTree (const T& t)
796  : Data_ (t)
797  {
798  }
799 
800  template<typename ObjT>
801  QString ToSql (ToSqlState<ObjT>& state) const
802  {
803  const auto& name = ":bound_" + QString::number (++state.LastID_);
804  state.BoundMembers_ [name] = ToVariantF (Data_);
805  return name;
806  }
807 
808  template<typename>
809  QSet<QString> AdditionalTables () const
810  {
811  return {};
812  }
813  };
814 
815  template<>
816  class ExprTree<ExprType::ConstTrue, void, void> {};
817 
819 
820  template<typename T>
821  constexpr auto AsLeafData (const T& node)
822  {
823  if constexpr (IsExprTree<T> {})
824  return node;
825  else
826  return ExprTree<ExprType::LeafData, T> { node };
827  }
828 
829  template<auto Ptr>
830  template<typename R>
831  auto ExprTree<ExprType::LeafStaticPlaceholder, MemberPtrs<Ptr>, void>::operator= (const R& r) const
832  {
833  return AssignList { *this, AsLeafData (r) };
834  }
835 
836  template<ExprType Type, typename L, typename R>
837  ExprTree<Type, L, R> MakeExprTree (const L& left, const R& right)
838  {
839  return { left, right };
840  }
841 
842  template<typename L, typename R>
843  using EnableRelOp_t = std::enable_if_t<AnyOf<IsExprTree, L, R>>;
844 
845  template<typename L, typename R>
846  constexpr auto AllTrees_v = AllOf<IsExprTree, L, R>;
847 
848  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
849  auto operator< (const L& left, const R& right)
850  {
851  if constexpr (AllTrees_v<L, R>)
852  return MakeExprTree<ExprType::Less> (left, right);
853  else
854  return AsLeafData (left) < AsLeafData (right);
855  }
856 
857  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
858  auto operator> (const L& left, const R& right)
859  {
860  if constexpr (AllTrees_v<L, R>)
861  return MakeExprTree<ExprType::Greater> (left, right);
862  else
863  return AsLeafData (left) > AsLeafData (right);
864  }
865 
866  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
867  auto operator== (const L& left, const R& right)
868  {
869  if constexpr (AllTrees_v<L, R>)
870  return MakeExprTree<ExprType::Equal> (left, right);
871  else
872  return AsLeafData (left) == AsLeafData (right);
873  }
874 
875  template<typename L, typename R, typename = EnableRelOp_t<L, R>>
876  auto operator&& (const L& left, const R& right)
877  {
878  if constexpr (AllTrees_v<L, R>)
879  return MakeExprTree<ExprType::And> (left, right);
880  else
881  return AsLeafData (left) && AsLeafData (right);
882  }
883 
884  template<typename>
885  auto HandleExprTree (const ExprTree<ExprType::ConstTrue>&, int lastId = 0)
886  {
887  return std::tuple { QString {}, Void {}, lastId };
888  }
889 
890  template<typename Seq, typename Tree,
891  typename = decltype (std::declval<Tree> ().ToSql (std::declval<ToSqlState<Seq>&> ()))>
892  auto HandleExprTree (const Tree& tree, int lastId = 0)
893  {
894  ToSqlState<Seq> state { lastId, {} };
895 
896  const auto& sql = tree.ToSql (state);
897 
898  return std::tuple
899  {
900  sql,
901  [state] (QSqlQuery& query)
902  {
903  for (const auto& pair : Stlize (state.BoundMembers_))
904  query.bindValue (pair.first, pair.second);
905  },
906  state.LastID_
907  };
908  }
909 
910  enum class AggregateFunction
911  {
912  Count
913  };
914 
915  template<AggregateFunction>
916  struct AggregateType {};
917  }
918 
919  namespace sph
920  {
921  template<int Idx>
923 
924  static constexpr pos<0> _0 = {};
925  static constexpr pos<1> _1 = {};
926  static constexpr pos<2> _2 = {};
927  static constexpr pos<3> _3 = {};
928  static constexpr pos<4> _4 = {};
929 
930  template<auto Ptr>
932 
933  template<auto... Ptrs>
934  constexpr detail::MemberPtrs<Ptrs...> fields {};
935 
937  };
938 
939  namespace detail
940  {
941  template<auto... Ptrs, size_t... Idxs>
942  auto MakeIndexedQueryHandler (detail::MemberPtrs<Ptrs...>, std::index_sequence<Idxs...>)
943  {
944  return [] (const QSqlQuery& q)
945  {
946  if constexpr (sizeof... (Ptrs) == 1)
948  else
949  return std::tuple { FromVariant<UnwrapIndirect_t<MemberPtrType_t<Ptrs>>> {} (q.value (Idxs))... };
950  };
951  }
952 
953  template<auto... Ptrs>
954  QStringList BuildFieldNames ()
955  {
956  return { BuildCachedFieldsData<MemberPtrStruct_t<Ptrs>> ().QualifiedFields_.value (FieldIndex<Ptrs> ())... };
957  }
958 
959  enum class SelectBehaviour { Some, One };
960 
961  template<typename T, SelectBehaviour SelectBehaviour>
963  {
964  const QSqlDatabase DB_;
965  const CachedFieldsData Cached_;
966 
967  struct SelectWhole {};
968  public:
969  SelectWrapper (const QSqlDatabase& db, const CachedFieldsData& data)
970  : DB_ { db }
971  , Cached_ (data)
972  {
973  }
974 
975  auto operator() () const
976  {
977  return (*this) (ConstTrueTree_v);
978  }
979 
980  template<typename Single>
981  auto operator() (Single&& single) const
982  {
983  if constexpr (IsExprTree<std::decay_t<Single>> {})
984  return (*this) (SelectWhole {}, std::forward<Single> (single));
985  else
986  return (*this) (std::forward<Single> (single), ConstTrueTree_v);
987  }
988 
989  template<typename Selector, ExprType Type, typename L, typename R>
990  auto operator() (Selector&& selector, const ExprTree<Type, L, R>& tree) const
991  {
992  const auto& [where, binder, _] = HandleExprTree<T> (tree);
993  Q_UNUSED (_);
994  const auto& [fields, initializer, postproc] = HandleSelector (std::forward<Selector> (selector));
995  return postproc (Select (fields, BuildFromClause (tree), where, binder, initializer));
996  }
997  private:
998  template<typename Binder, typename Initializer>
999  auto Select (const QString& fields, const QString& from, QString where,
1000  Binder&& binder, Initializer&& initializer) const
1001  {
1002  if (!where.isEmpty ())
1003  where.prepend (" WHERE ");
1004 
1005  const auto& queryStr = "SELECT " + fields +
1006  " FROM " + from +
1007  where;
1008 
1009  QSqlQuery query { DB_ };
1010  query.prepare (queryStr);
1011  if constexpr (!std::is_same_v<Void, std::decay_t<Binder>>)
1012  binder (query);
1013 
1014  if (!query.exec ())
1015  throw QueryException ("fetch query execution failed", std::make_shared<QSqlQuery> (query));
1016 
1017  if constexpr (SelectBehaviour == SelectBehaviour::Some)
1018  {
1020  while (query.next ())
1021  result << initializer (query);
1022  return result;
1023  }
1024  else
1025  {
1026  using RetType_t = boost::optional<std::result_of_t<Initializer (QSqlQuery)>>;
1027  return query.next () ?
1028  RetType_t { initializer (query) } :
1029  RetType_t {};
1030  }
1031  }
1032 
1033  template<ExprType Type, typename L, typename R>
1034  QString BuildFromClause (const ExprTree<Type, L, R>& tree) const
1035  {
1036  if constexpr (Type != ExprType::ConstTrue)
1037  {
1038  const auto& additionalTables = Util::MapAs<QList> (tree.template AdditionalTables<T> (),
1039  [] (const QString& table) { return ", " + table; });
1040  return Cached_.Table_ + additionalTables.join (QString {});
1041  }
1042  else
1043  return Cached_.Table_;
1044  }
1045 
1046  auto HandleSelector (SelectWhole) const
1047  {
1048  return std::tuple
1049  {
1050  Cached_.QualifiedFields_.join (", "),
1051  [] (const QSqlQuery& q) { return InitializeFromQuery<T> (q, SeqIndices<T>); },
1052  Id
1053  };
1054  }
1055 
1056  template<int Idx>
1057  auto HandleSelector (sph::pos<Idx>) const
1058  {
1059  return std::tuple
1060  {
1061  Cached_.QualifiedFields_.value (Idx),
1062  [] (const QSqlQuery& q) { return FromVariant<UnwrapIndirect_t<ValueAtC_t<T, Idx>>> {} (q.value (0)); },
1063  Id
1064  };
1065  }
1066 
1067  template<auto... Ptrs>
1068  auto HandleSelector (MemberPtrs<Ptrs...> ptrs) const
1069  {
1070  return std::tuple
1071  {
1072  BuildFieldNames<Ptrs...> ().join (", "),
1073  MakeIndexedQueryHandler (ptrs, std::make_index_sequence<sizeof... (Ptrs)> {}),
1074  Id
1075  };
1076  }
1077 
1078  template<AggregateFunction Fun>
1079  auto HandleSelector (AggregateType<Fun>) const
1080  {
1081  if constexpr (Fun == AggregateFunction::Count)
1082  return std::tuple
1083  {
1084  QString { "count(1)" },
1085  [] (const QSqlQuery& q) { return q.value (0).toLongLong (); },
1086  [] (const QList<long long>& list) { return list.value (0); }
1087  };
1088  }
1089  };
1090 
1091  template<typename T>
1093  {
1094  const QSqlDatabase DB_;
1095  const CachedFieldsData Cached_;
1096  public:
1097  DeleteByFieldsWrapper (const QSqlDatabase& db, const CachedFieldsData& data)
1098  : DB_ { db }
1099  , Cached_ (data)
1100  {
1101  }
1102 
1103  template<ExprType Type, typename L, typename R>
1104  void operator() (const ExprTree<Type, L, R>& tree) const
1105  {
1106  const auto& [where, binder, _] = HandleExprTree<T> (tree);
1107  Q_UNUSED (_);
1108 
1109  const auto& selectAll = "DELETE FROM " + Cached_.Table_ +
1110  " WHERE " + where + ";";
1111 
1112  QSqlQuery query { DB_ };
1113  query.prepare (selectAll);
1114  binder (query);
1115  query.exec ();
1116  }
1117  };
1118 
1119  template<typename T, bool HasPKey = HasPKey<T>>
1121  {
1122  const QSqlDatabase DB_;
1123  const CachedFieldsData Cached_;
1124 
1125  std::function<void (T)> Updater_;
1126  public:
1127  AdaptUpdate (const QSqlDatabase& db, const CachedFieldsData& data)
1128  : DB_ { db }
1129  , Cached_ { data }
1130  {
1131  if constexpr (HasPKey)
1132  {
1133  const auto index = FindPKey<T>::result_type::value;
1134 
1135  QList<QString> removedFields { data.Fields_ };
1136  QList<QString> removedBoundFields { data.BoundFields_ };
1137 
1138  const auto& fieldName = removedFields.takeAt (index);
1139  const auto& boundName = removedBoundFields.takeAt (index);
1140 
1141  const auto& statements = Util::ZipWith (removedFields, removedBoundFields,
1142  [] (const QString& s1, const QString& s2) { return s1 + " = " + s2; });
1143 
1144  const auto& update = "UPDATE " + data.Table_ +
1145  " SET " + statements.join (", ") +
1146  " WHERE " + fieldName + " = " + boundName + ";";
1147 
1148  const auto updateQuery = std::make_shared<QSqlQuery> (db);
1149  updateQuery->prepare (update);
1150  Updater_ = MakeInserter<T> (data, updateQuery, true);
1151  }
1152  }
1153 
1154  template<bool B = HasPKey>
1155  std::enable_if_t<B> operator() (const T& seq)
1156  {
1157  Updater_ (seq);
1158  }
1159 
1160  template<typename SL, typename SR, ExprType WType, typename WL, typename WR>
1162  {
1163  const auto& [setClause, setBinder, setLast] = HandleExprTree<T> (set);
1164  const auto& [whereClause, whereBinder, _] = HandleExprTree<T> (where, setLast);
1165 
1166  const auto& update = "UPDATE " + Cached_.Table_ +
1167  " SET " + setClause +
1168  " WHERE " + whereClause;
1169 
1170  QSqlQuery query { DB_ };
1171  query.prepare (update);
1172  setBinder (query);
1173  whereBinder (query);
1174  query.exec ();
1175  }
1176  };
1177 
1178  template<typename T>
1180 
1181  template<typename T>
1183 
1184  template<typename T>
1186 
1187  template<int... Fields>
1189  {
1190  QString operator() (const CachedFieldsData& data) const
1191  {
1192  return "UNIQUE (" + QStringList { data.Fields_.value (Fields)... }.join (", ") + ")";
1193  }
1194  };
1195 
1196  template<int... Fields>
1197  struct ConstraintToString<PrimaryKey<Fields...>>
1198  {
1199  QString operator() (const CachedFieldsData& data) const
1200  {
1201  return "PRIMARY KEY (" + QStringList { data.Fields_.value (Fields)... }.join (", ") + ")";
1202  }
1203  };
1204 
1205  template<typename... Args>
1207  {
1208  return { ConstraintToString<Args> {} (data)... };
1209  }
1210 
1211  template<typename T, size_t... Indices>
1212  QList<QString> GetTypes (std::index_sequence<Indices...>)
1213  {
1214  return { Type2Name<ValueAtC_t<T, Indices>> {} ()... };
1215  }
1216 
1217  template<typename T>
1218  QString AdaptCreateTable (const CachedFieldsData& data)
1219  {
1220  const auto& types = GetTypes<T> (SeqIndices<T>);
1221 
1222  const auto& constraints = GetConstraintsStringList (ConstraintsType<T> {}, data);
1223  const auto& constraintsStr = constraints.isEmpty () ?
1224  QString {} :
1225  (", " + constraints.join (", "));
1226 
1227  const auto& statements = Util::ZipWith (types, static_cast<const QList<QString>&> (data.Fields_),
1228  [] (const QString& type, const QString& field) { return field + " " + type; });
1229  return "CREATE TABLE " +
1230  data.Table_ +
1231  " (" +
1232  statements.join (", ") +
1233  constraintsStr +
1234  ");";
1235  }
1236  }
1237 
1238  template<typename T>
1239  struct ObjectInfo
1240  {
1244 
1248  };
1249 
1250  template<typename T>
1251  ObjectInfo<T> Adapt (const QSqlDatabase& db)
1252  {
1253  const auto& cachedData = detail::BuildCachedFieldsData<T> ();
1254 
1255  if (db.record (cachedData.Table_).isEmpty ())
1256  RunTextQuery (db, detail::AdaptCreateTable<T> (cachedData));
1257 
1258  return
1259  {
1260  { db, cachedData },
1261  { db, cachedData },
1262  { db, cachedData },
1263  { db, cachedData },
1264  { db, cachedData },
1265  { db, cachedData }
1266  };
1267  }
1268 
1269  template<typename T>
1270  using ObjectInfo_ptr = std::shared_ptr<ObjectInfo<T>>;
1271 
1272  template<typename T>
1273  ObjectInfo_ptr<T> AdaptPtr (const QSqlDatabase& db)
1274  {
1275  return std::make_shared<ObjectInfo<T>> (Adapt<T> (db));
1276  }
1277 }
1278 }
1279 }
std::enable_if_t< AnyOf< IsExprTree, L, R > > EnableRelOp_t
Definition: oral.h:843
detail::DeleteByFieldsWrapper< T > DeleteBy
Definition: oral.h:1247
constexpr detail::AggregateType< detail::AggregateFunction::Count > count
Definition: oral.h:936
QString operator()() const
Definition: oral.h:195
constexpr detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, detail::MemberPtrs< Ptr > > f
Definition: oral.h:931
auto Stlize(Assoc &&assoc) -> detail::StlAssocRange< detail::Identity, detail::Identity, decltype(assoc.begin()), Assoc, PairType >
Converts an Qt&#39;s associative sequence assoc to an STL-like iteratable range.
Definition: qtutil.h:166
detail::ExprTree< detail::ExprType::LeafStaticPlaceholder, boost::mpl::int_< Idx > > pos
Definition: oral.h:922
std::array< QSqlQuery_ptr, InsertActionCount > Queries_
Definition: oral.h:407
AdaptInsert(const QSqlDatabase &db, CachedFieldsData data)
Definition: oral.h:409
const CachedFieldsData Data_
Definition: oral.h:402
const auto Id
Definition: prelude.h:249
std::shared_ptr< ObjectInfo< T > > ObjectInfo_ptr
Definition: oral.h:1270
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:365
constexpr size_t InsertActionCount
Definition: oral.h:285
Util::IsDetected_t< Constraints<>, ConstraintsDetector, T > ConstraintsType
Definition: oral.h:1182
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:65
AdaptDelete(const QSqlDatabase &, const CachedFieldsData &, std::enable_if_t<!B > *=nullptr)
Definition: oral.h:498
QString TypeToSql(ExprType type)
Definition: oral.h:587
typename detail::DecomposeMemberPtr< decltype(Ptr)>::StructType_t MemberPtrStruct_t
Definition: typegetter.h:96
auto operator==(const L &left, const R &right)
Definition: oral.h:867
QVariant ToVariantF(const T &t)
Definition: oral.h:290
typename T::Constraints ConstraintsDetector
Definition: oral.h:1179
T InitializeFromQuery(const QSqlQuery &q, std::index_sequence< Indices... >)
Definition: oral.h:513
const QSqlQuery & GetQuery() const
Definition: oral.h:92
STL namespace.
static constexpr auto Ptr()
Definition: oral.h:158
detail::SelectWrapper< T, detail::SelectBehaviour::Some > Select
Definition: oral.h:1245
Typelist< Args... > Constraints
Definition: oraltypes.h:216
constexpr auto AllTrees_v
Definition: oral.h:846
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:647
SelectWrapper(const QSqlDatabase &db, const CachedFieldsData &data)
Definition: oral.h:969
void operator()(const HeadArg &arg, const TailArgs &... tail) const
Definition: oral.h:549
auto operator()(Seq &t, InsertAction action=InsertAction::Default) const
Definition: oral.h:430
void Unreachable()
Definition: unreachable.h:36
QSet< QString > AdditionalTables() const
Definition: oral.h:723
auto HandleExprTree(const ExprTree< ExprType::ConstTrue > &, int lastId=0)
Definition: oral.h:885
constexpr bool IsDetected_v
Definition: detector.h:56
typename detail::IsDetected< Type, void, Op, Args... >::type IsDetected_t
Definition: detector.h:59
std::function< void(Seq)> Deleter_
Definition: oral.h:474
AssignList(const L &l, const R &r)
Definition: oral.h:679
QString AdaptCreateTable(const CachedFieldsData &data)
Definition: oral.h:1218
static constexpr bool HasAutogen_
Definition: oral.h:405
constexpr auto HasPKey
Definition: oral.h:372
constexpr auto ConstTrueTree_v
Definition: oral.h:818
typename boost::fusion::result_of::value_at< Seq, Idx >::type ValueAt_t
Definition: oral.h:347
detail::SelectWrapper< T, detail::SelectBehaviour::One > SelectOne
Definition: oral.h:1246
detail::AdaptDelete< T > Delete
Definition: oral.h:1243
ObjectInfo< T > Adapt(const QSqlDatabase &db)
Definition: oral.h:1251
DeleteByFieldsWrapper(const QSqlDatabase &db, const CachedFieldsData &data)
Definition: oral.h:1097
std::enable_if_t< B > operator()(const T &seq)
Definition: oral.h:1155
CachedFieldsData BuildCachedFieldsData(const QString &table)
Definition: oral.h:305
detail::AdaptInsert< T > Insert
Definition: oral.h:1241
auto MakeIndexedQueryHandler(detail::MemberPtrs< Ptrs... >, std::index_sequence< Idxs... >)
Definition: oral.h:942
Type
Describes the various types of XDG .desktop files.
Definition: itemtypes.h:48
typename boost::fusion::result_of::value_at_c< Seq, Idx >::type ValueAtC_t
Definition: oral.h:344
AdaptDelete(const QSqlDatabase &db, const CachedFieldsData &data, std::enable_if_t< B > *=nullptr)
Definition: oral.h:477
QList< QString > GetTypes(std::index_sequence< Indices... >)
Definition: oral.h:1212
QStringList GetConstraintsStringList(Constraints< Args... >, const CachedFieldsData &data)
Definition: oral.h:1206
QStringList BuildFieldNames()
Definition: oral.h:954
A proper void type, akin to unit (or ()) type in functional languages.
Definition: void.h:41
constexpr auto SeqIndices
Definition: oral.h:129
ObjectInfo_ptr< T > AdaptPtr(const QSqlDatabase &db)
Definition: oral.h:1273
const QSqlQuery_ptr & GetQueryPtr() const
Definition: oral.h:87
constexpr size_t FieldIndex()
Definition: oral.h:171
auto MakeInserter(const CachedFieldsData &data, const QSqlQuery_ptr &insertQuery, bool bindPrimaryKey)
Definition: oral.h:322
detail::AdaptUpdate< T > Update
Definition: oral.h:1242
QString ToSql(ToSqlState< T > &state) const
Definition: oral.h:686
constexpr auto HasAutogenPKey()
Definition: oral.h:375
typename detail::DecomposeMemberPtr< decltype(Ptr)>::Value_t MemberPtrType_t
Definition: typegetter.h:93
static constexpr auto Index()
Definition: oral.h:164
QString MorphFieldName(QString str)
Definition: oral.h:104
constexpr auto SeqSize
Definition: oral.h:126
std::conditional_t< std::is_same_v< detail::RetTypeRaw_t< F >, detail::ReturnsVoid >, void, detail::RetTypeRaw_t< F > > RetType_t
Definition: typegetter.h:77
auto operator>(const L &left, const R &right)
Definition: oral.h:858
typename std::conditional_t< IsIndirect< T > {}, T, WrapDirect< T > >::value_type UnwrapIndirect_t
Definition: oral.h:643
QSqlQuery RunTextQuery(const QSqlDatabase &db, const QString &text)
Runs the given query text on the given db.
Definition: util.cpp:40
auto operator<(const L &left, const R &right)
Definition: oral.h:849
constexpr bool IsRelational(ExprType type)
Definition: oral.h:617
decltype(new T { std::declval< Args >()... }) AggregateDetector_t
Definition: oral.h:510
auto Map(Container &&c, F f)
Definition: prelude.h:165
std::enable_if_t< B > operator()(const Seq &seq)
Definition: oral.h:503
constexpr detail::MemberPtrs< Ptrs... > fields
Definition: oral.h:934
T operator()(const QVariant &var) const
Definition: oral.h:265
decltype(std::declval< U >().FieldNameMorpher(QString {})) MorpherDetector
Definition: oral.h:101
ExprTree(const L &l, const R &r)
Definition: oral.h:707
auto operator,(const AssignList< OL, OR > &tail)
Definition: oral.h:695
constexpr auto AsLeafData(const T &node)
Definition: oral.h:821
auto operator &&(const L &left, const R &right)
Definition: oral.h:876
static UTIL_DB_API void DumpError(const QSqlError &error)
Dumps the error to the qWarning() stream.
Definition: dblock.cpp:84
ExprTree< Type, L, R > MakeExprTree(const L &left, const R &right)
Definition: oral.h:837
std::shared_ptr< QSqlQuery > QSqlQuery_ptr
Definition: oral.h:65
QueryException(const std::string &str, const QSqlQuery_ptr &q)
Definition: oral.h:77
QString ToSql(ToSqlState< T > &state) const
Definition: oral.h:714
typename AsTypelist< T >::Result_t AsTypelist_t
Definition: typelist.h:182
void operator()(const ExprTree< Type, L, R > &tree) const
Definition: oral.h:1104
QString GetInsertPrefix(InsertAction action)
Definition: oral.h:383
boost::mpl::int_< FindPKey< Seq >::result_type::value > FindPKeyDetector
Definition: oral.h:369
QVariant operator()(const T &t) const
Definition: oral.h:249
AdaptUpdate(const QSqlDatabase &db, const CachedFieldsData &data)
Definition: oral.h:1127
constexpr auto AreComparableTypes
Definition: oral.h:650
FieldsUnpacker< TailT... > Tail_t
Definition: oral.h:533