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