drogon
C++14/17-based HTTP application framework
Loading...
Searching...
No Matches
Mapper.h
Go to the documentation of this file.
1
14
15#pragma once
16#include <drogon/orm/Criteria.h>
18#include <drogon/orm/DbClient.h>
20#include <string>
21#include <type_traits>
22#include <vector>
23
24#ifdef _WIN32
25using ssize_t = std::intptr_t;
26#endif
27
28namespace drogon
29{
30namespace orm
31{
32enum class SortOrder
33{
34 ASC,
35 DESC
36};
37
38namespace internal
39{
40template <typename T, bool hasPrimaryKey = true>
41struct Traits
42{
43 using type = typename T::PrimaryKeyType;
44};
45
46template <typename T>
47struct Traits<T, false>
48{
49 using type = int;
50};
51
52template <typename T>
54{
55 private:
56 using yes = std::true_type;
57 using no = std::false_type;
58
59 template <typename U>
60 static auto test(int) -> decltype(U::sqlForFindingByPrimaryKey(), yes());
61
62 template <typename>
63 static no test(...);
64
65 public:
66 static constexpr bool value = std::is_same_v<decltype(test<T>(0)), yes>;
67};
68
69template <typename T>
71{
72 private:
73 using yes = std::true_type;
74 using no = std::false_type;
75
76 template <typename U>
77 static auto test(int)
78 -> decltype(U::sqlForDeletingByPrimaryKey().length(), yes());
79
80 template <typename>
81 static no test(...);
82
83 public:
84 static constexpr bool value = std::is_same_v<decltype(test<T>(0)), yes>;
85};
86} // namespace internal
87
115template <typename T>
117{
118 public:
124 explicit Mapper(DbClientPtr client) : client_(std::move(client))
125 {
126 }
127
134 Mapper<T> &limit(size_t limit);
135
142 Mapper<T> &offset(size_t offset);
143
151 Mapper<T> &orderBy(const std::string &colName,
152 const SortOrder &order = SortOrder::ASC);
153
161 Mapper<T> &orderBy(size_t colIndex,
162 const SortOrder &order = SortOrder::ASC);
163
173 Mapper<T> &paginate(size_t page, size_t perPage);
174
181
190 Mapper<T> &innerJoin(const std::string &table,
191 const std::string &onLeft,
192 const std::string &onRight)
193 {
194 assert(isValidSqlIdentifier(table));
195 assert(isValidSqlIdentifier(onLeft));
196 assert(isValidSqlIdentifier(onRight));
197 joinString_ += " INNER JOIN ";
198 joinString_ += table;
199 joinString_ += " ON ";
200 joinString_ += onLeft;
201 joinString_ += " = ";
202 joinString_ += onRight;
203 return *this;
204 }
205
214 Mapper<T> &leftJoin(const std::string &table,
215 const std::string &onLeft,
216 const std::string &onRight)
217 {
218 assert(isValidSqlIdentifier(table));
219 assert(isValidSqlIdentifier(onLeft));
220 assert(isValidSqlIdentifier(onRight));
221 joinString_ += " LEFT JOIN ";
222 joinString_ += table;
223 joinString_ += " ON ";
224 joinString_ += onLeft;
225 joinString_ += " = ";
226 joinString_ += onRight;
227 return *this;
228 }
229
238 Mapper<T> &rightJoin(const std::string &table,
239 const std::string &onLeft,
240 const std::string &onRight)
241 {
242 assert(isValidSqlIdentifier(table));
243 assert(isValidSqlIdentifier(onLeft));
244 assert(isValidSqlIdentifier(onRight));
245 joinString_ += " RIGHT JOIN ";
246 joinString_ += table;
247 joinString_ += " ON ";
248 joinString_ += onLeft;
249 joinString_ += " = ";
250 joinString_ += onRight;
251 return *this;
252 }
253
254 using SingleRowCallback = std::function<void(T)>;
255 using MultipleRowsCallback = std::function<void(std::vector<T>)>;
256 using CountCallback = std::function<void(const size_t)>;
257
258 using TraitsPKType = typename internal::
259 Traits<T, !std::is_same_v<typename T::PrimaryKeyType, void>>::type;
260
268
269 template <typename U = T>
270 inline T findByPrimaryKey(const TraitsPKType &key) noexcept(false)
271 {
272 if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)
273 {
274 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
275 "No primary key in the table!");
276 static_assert(
277 internal::has_sqlForFindingByPrimaryKey<T>::value,
278 "No function member named sqlForFindingByPrimaryKey, please "
279 "make sure that the model class is generated by the latest "
280 "version of drogon_ctl");
281 // return findOne(Criteria(T::primaryKeyName, key));
282 std::string sql = T::sqlForFindingByPrimaryKey();
283 if (forUpdate_)
284 {
285 sql += " for update";
286 }
287 clear();
288 Result r(nullptr);
289 {
290 auto binder = *client_ << std::move(sql);
291 outputPrimaryKeyToBinder(key, binder);
292 binder << Mode::Blocking;
293 binder >> [&r](const Result &result) { r = result; };
294 binder.exec(); // exec may be throw exception;
295 }
296 if (r.size() == 0)
297 {
298 throw UnexpectedRows("0 rows found");
299 }
300 else if (r.size() > 1)
301 {
302 throw UnexpectedRows("Found more than one row");
303 }
304 auto row = r[0];
305 return T(row);
306 }
307 else
308 {
309 LOG_FATAL << "The table must have a primary key";
310 abort();
311 }
312 }
313
321 template <typename U = T>
322 inline void findByPrimaryKey(const TraitsPKType &key,
323 const SingleRowCallback &rcb,
324 const ExceptionCallback &ecb) noexcept
325 {
326 if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)
327 {
328 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
329 "No primary key in the table!");
330 static_assert(
331 internal::has_sqlForFindingByPrimaryKey<T>::value,
332 "No function member named sqlForFindingByPrimaryKey, please "
333 "make sure that the model class is generated by the latest "
334 "version of drogon_ctl");
335 // findOne(Criteria(T::primaryKeyName, key), rcb, ecb);
336 std::string sql = T::sqlForFindingByPrimaryKey();
337 if (forUpdate_)
338 {
339 sql += " for update";
340 }
341 clear();
342 auto binder = *client_ << std::move(sql);
343 outputPrimaryKeyToBinder(key, binder);
344 binder >> [ecb, rcb](const Result &r) {
345 if (r.size() == 0)
346 {
347 ecb(UnexpectedRows("0 rows found"));
348 }
349 else if (r.size() > 1)
350 {
351 ecb(UnexpectedRows("Found more than one row"));
352 }
353 else
354 {
355 rcb(T(r[0]));
356 }
357 };
358 binder >> ecb;
359 }
360 else
361 {
362 LOG_FATAL << "The table must have a primary key";
363 abort();
364 }
365 }
366
376 template <typename U = T>
377 inline std::future<T> findFutureByPrimaryKey(
378 const TraitsPKType &key) noexcept
379 {
380 if constexpr (!std::is_same_v<typename U::PrimaryKeyType, void>)
381 {
382 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
383 "No primary key in the table!");
384 static_assert(
385 internal::has_sqlForFindingByPrimaryKey<T>::value,
386 "No function member named sqlForFindingByPrimaryKey, please "
387 "make sure that the model class is generated by the latest "
388 "version of drogon_ctl");
389 // return findFutureOne(Criteria(T::primaryKeyName, key));
390 std::string sql = T::sqlForFindingByPrimaryKey();
391 if (forUpdate_)
392 {
393 sql += " for update";
394 }
395 clear();
396 auto binder = *client_ << std::move(sql);
397 outputPrimaryKeyToBinder(key, binder);
398
399 std::shared_ptr<std::promise<T>> prom =
400 std::make_shared<std::promise<T>>();
401 binder >> [prom](const Result &r) {
402 if (r.size() == 0)
403 {
404 prom->set_exception(std::make_exception_ptr(
405 UnexpectedRows("0 rows found")));
406 }
407 else if (r.size() > 1)
408 {
409 prom->set_exception(std::make_exception_ptr(
410 UnexpectedRows("Found more than one row")));
411 }
412 else
413 {
414 prom->set_value(T(r[0]));
415 }
416 };
417 binder >>
418 [prom](const std::exception_ptr &e) { prom->set_exception(e); };
419 binder.exec();
420 return prom->get_future();
421 }
422 else
423 {
424 LOG_FATAL << "The table must have a primary key";
425 abort();
426 }
427 }
428
434 std::vector<T> findAll() noexcept(false);
435
442 void findAll(const MultipleRowsCallback &rcb,
443 const ExceptionCallback &ecb) noexcept;
444
451 std::future<std::vector<T>> findFutureAll() noexcept;
452
459 size_t count(const Criteria &criteria = Criteria()) noexcept(false);
460
469 void count(const Criteria &criteria,
470 const CountCallback &rcb,
471 const ExceptionCallback &ecb) noexcept;
472
481 std::future<size_t> countFuture(
482 const Criteria &criteria = Criteria()) noexcept;
483
492 T findOne(const Criteria &criteria) noexcept(false);
493
501 void findOne(const Criteria &criteria,
502 const SingleRowCallback &rcb,
503 const ExceptionCallback &ecb) noexcept;
504
515 std::future<T> findFutureOne(const Criteria &criteria) noexcept;
516
523 std::vector<T> findBy(const Criteria &criteria) noexcept(false);
524
532 void findBy(const Criteria &criteria,
533 const MultipleRowsCallback &rcb,
534 const ExceptionCallback &ecb) noexcept;
535
543 std::future<std::vector<T>> findFutureBy(const Criteria &criteria) noexcept;
544
552 void insert(T &obj) noexcept(false);
553
562 void insert(const T &obj,
563 const SingleRowCallback &rcb,
564 const ExceptionCallback &ecb) noexcept;
565
572 std::future<T> insertFuture(const T &) noexcept;
573
581 size_t update(const T &obj) noexcept(false);
582
591 void update(const T &obj,
592 const CountCallback &rcb,
593 const ExceptionCallback &ecb) noexcept;
594
603 std::future<size_t> updateFuture(const T &obj) noexcept;
604
615 template <typename... Arguments>
616 size_t updateBy(const std::vector<std::string> &colNames,
617 const Criteria &criteria,
618 Arguments &&...args) noexcept(false);
619
630 template <typename... Arguments>
631 void updateBy(const std::vector<std::string> &colNames,
632 const CountCallback &rcb,
633 const ExceptionCallback &ecb,
634 const Criteria &criteria,
635 Arguments &&...args) noexcept;
636
648 template <typename... Arguments>
649 std::future<size_t> updateFutureBy(const std::vector<std::string> &colNames,
650 const Criteria &criteria,
651 Arguments &&...args) noexcept;
652
662 template <typename... Arguments>
663 size_t increment(const std::vector<std::string> &colNames,
664 const Criteria &criteria,
665 Arguments... args) noexcept(false);
666
677 template <typename... Arguments>
678 void increment(const std::vector<std::string> &colNames,
679 const CountCallback &rcb,
680 const ExceptionCallback &ecb,
681 const Criteria &criteria,
682 Arguments... args) noexcept;
683
694 template <typename... Arguments>
695 std::future<size_t> incrementFuture(
696 const std::vector<std::string> &colNames,
697 const Criteria &criteria,
698 Arguments... args) noexcept;
699
707 size_t deleteOne(const T &obj) noexcept(false);
708
717 void deleteOne(const T &obj,
718 const CountCallback &rcb,
719 const ExceptionCallback &ecb) noexcept;
720
729 std::future<size_t> deleteFutureOne(const T &obj) noexcept;
730
737 size_t deleteBy(const Criteria &criteria) noexcept(false);
738
746 void deleteBy(const Criteria &criteria,
747 const CountCallback &rcb,
748 const ExceptionCallback &ecb) noexcept;
749
757 std::future<size_t> deleteFutureBy(const Criteria &criteria) noexcept;
758
765 size_t deleteByPrimaryKey(const TraitsPKType &key) noexcept(false);
766
775 void deleteByPrimaryKey(const TraitsPKType &key,
776 const CountCallback &rcb,
777 const ExceptionCallback &ecb) noexcept;
778
787 std::future<size_t> deleteFutureByPrimaryKey(
788 const TraitsPKType &key) noexcept;
789
790 protected:
791 DbClientPtr client_;
792 size_t limit_{0};
793 size_t offset_{0};
794 std::string orderByString_;
795 std::string joinString_;
796 bool forUpdate_{false};
797
798 void clear()
799 {
800 limit_ = 0;
801 offset_ = 0;
802 orderByString_.clear();
803 joinString_.clear();
804 forUpdate_ = false;
805 }
806
807 template <typename PKType = decltype(T::primaryKeyName)>
808 void makePrimaryKeyCriteria(std::string &sql)
809 {
810 if constexpr (std::is_same_v<const std::string, PKType>)
811 {
812 sql += " where ";
813 sql += T::primaryKeyName;
814 sql += " = $?";
815 }
816 else if constexpr (std::is_same_v<const std::vector<std::string>,
817 PKType>)
818 {
819 sql += " where ";
820 for (size_t i = 0; i < T::primaryKeyName.size(); ++i)
821 {
822 sql += T::primaryKeyName[i];
823 sql += " = $?";
824 if (i < (T::primaryKeyName.size() - 1))
825 {
826 sql += " and ";
827 }
828 }
829 }
830 }
831
832 template <typename PKType = decltype(T::primaryKeyName)>
833 void outputPrimaryKeyToBinder(const TraitsPKType &pk,
834 internal::SqlBinder &binder)
835 {
836 if constexpr (std::is_same_v<const std::string, PKType>)
837 {
838 binder << pk;
839 }
840 else if constexpr (std::is_same_v<const std::vector<std::string>,
841 PKType>)
842 {
843 tupleToBinder<typename T::PrimaryKeyType>(pk, binder);
844 }
845 }
846
847 template <typename TP, ssize_t N = std::tuple_size<TP>::value>
848 void tupleToBinder(const TP &t, internal::SqlBinder &binder)
849 {
850 if constexpr (N > 1)
851 {
852 tupleToBinder<TP, N - 1>(t, binder);
853 binder << std::get<N - 1>(t);
854 }
855 else if constexpr (N == 1)
856 {
857 binder << std::get<0>(t);
858 }
859 }
860
861 std::string replaceSqlPlaceHolder(const std::string &sqlStr,
862 const std::string &holderStr) const;
863};
864
865template <typename T>
866inline T Mapper<T>::findOne(const Criteria &criteria) noexcept(false)
867{
868 std::string sql = "select * from ";
869 sql += T::tableName;
870 sql += joinString_;
871 bool hasParameters = false;
872 if (criteria)
873 {
874 sql += " where ";
875 sql += criteria.criteriaString();
876 hasParameters = true;
877 }
878 sql.append(orderByString_);
879 if (limit_ > 0)
880 {
881 hasParameters = true;
882 sql.append(" limit $?");
883 }
884 if (offset_ > 0)
885 {
886 hasParameters = true;
887 sql.append(" offset $?");
888 }
889 if (hasParameters)
890 sql = replaceSqlPlaceHolder(sql, "$?");
891 if (forUpdate_)
892 {
893 sql += " for update";
894 }
895 Result r(nullptr);
896 {
897 auto binder = *client_ << std::move(sql);
898 if (criteria)
899 criteria.outputArgs(binder);
900 if (limit_ > 0)
901 binder << limit_;
902 if (offset_)
903 binder << offset_;
904 clear();
905 binder << Mode::Blocking;
906 binder >> [&r](const Result &result) { r = result; };
907 binder.exec(); // exec may be throw exception;
908 }
909 if (r.size() == 0)
910 {
911 throw UnexpectedRows("0 rows found");
912 }
913 else if (r.size() > 1)
914 {
915 throw UnexpectedRows("Found more than one row");
916 }
917 auto row = r[0];
918 return T(row);
919}
920
921template <typename T>
922inline void Mapper<T>::findOne(const Criteria &criteria,
923 const SingleRowCallback &rcb,
924 const ExceptionCallback &ecb) noexcept
925{
926 std::string sql = "select * from ";
927 sql += T::tableName;
928 sql += joinString_;
929 bool hasParameters = false;
930 if (criteria)
931 {
932 sql += " where ";
933 sql += criteria.criteriaString();
934 hasParameters = true;
935 }
936 sql.append(orderByString_);
937 if (limit_ > 0)
938 {
939 hasParameters = true;
940 sql.append(" limit $?");
941 }
942 if (offset_ > 0)
943 {
944 hasParameters = true;
945 sql.append(" offset $?");
946 }
947 if (hasParameters)
948 sql = replaceSqlPlaceHolder(sql, "$?");
949 if (forUpdate_)
950 {
951 sql += " for update";
952 }
953 auto binder = *client_ << std::move(sql);
954 if (criteria)
955 criteria.outputArgs(binder);
956 if (limit_ > 0)
957 binder << limit_;
958 if (offset_)
959 binder << offset_;
960 clear();
961 binder >> [ecb, rcb](const Result &r) {
962 if (r.size() == 0)
963 {
964 ecb(UnexpectedRows("0 rows found"));
965 }
966 else if (r.size() > 1)
967 {
968 ecb(UnexpectedRows("Found more than one row"));
969 }
970 else
971 {
972 rcb(T(r[0]));
973 }
974 };
975 binder >> ecb;
976}
977
978template <typename T>
979inline std::future<T> Mapper<T>::findFutureOne(
980 const Criteria &criteria) noexcept
981{
982 std::string sql = "select * from ";
983 sql += T::tableName;
984 sql += joinString_;
985 bool hasParameters = false;
986 if (criteria)
987 {
988 sql += " where ";
989 sql += criteria.criteriaString();
990 hasParameters = true;
991 }
992 sql.append(orderByString_);
993 if (limit_ > 0)
994 {
995 hasParameters = true;
996 sql.append(" limit $?");
997 }
998 if (offset_ > 0)
999 {
1000 hasParameters = true;
1001 sql.append(" offset $?");
1002 }
1003 if (hasParameters)
1004 sql = replaceSqlPlaceHolder(sql, "$?");
1005 if (forUpdate_)
1006 {
1007 sql += " for update";
1008 }
1009 auto binder = *client_ << std::move(sql);
1010 if (criteria)
1011 criteria.outputArgs(binder);
1012 if (limit_ > 0)
1013 binder << limit_;
1014 if (offset_)
1015 binder << offset_;
1016 clear();
1017 std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();
1018 binder >> [prom](const Result &r) {
1019 if (r.size() == 0)
1020 {
1021 prom->set_exception(
1022 std::make_exception_ptr(UnexpectedRows("0 rows found")));
1023 }
1024 else if (r.size() > 1)
1025 {
1026 prom->set_exception(std::make_exception_ptr(
1027 UnexpectedRows("Found more than one row")));
1028 }
1029 else
1030 {
1031 prom->set_value(T(r[0]));
1032 }
1033 };
1034 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1035 binder.exec();
1036 return prom->get_future();
1037}
1038
1039template <typename T>
1040inline std::vector<T> Mapper<T>::findBy(const Criteria &criteria) noexcept(
1041 false)
1042{
1043 std::string sql = "select * from ";
1044 sql += T::tableName;
1045 sql += joinString_;
1046 bool hasParameters = false;
1047 if (criteria)
1048 {
1049 hasParameters = true;
1050 sql += " where ";
1051 sql += criteria.criteriaString();
1052 }
1053 sql.append(orderByString_);
1054 if (limit_ > 0)
1055 {
1056 hasParameters = true;
1057 sql.append(" limit $?");
1058 }
1059 if (offset_ > 0)
1060 {
1061 hasParameters = true;
1062 sql.append(" offset $?");
1063 }
1064 if (hasParameters)
1065 sql = replaceSqlPlaceHolder(sql, "$?");
1066 if (forUpdate_)
1067 {
1068 sql += " for update";
1069 }
1070 Result r(nullptr);
1071 {
1072 auto binder = *client_ << std::move(sql);
1073 if (criteria)
1074 criteria.outputArgs(binder);
1075 if (limit_ > 0)
1076 binder << limit_;
1077 if (offset_)
1078 binder << offset_;
1079 clear();
1080 binder << Mode::Blocking;
1081 binder >> [&r](const Result &result) { r = result; };
1082 binder.exec(); // exec may be throw exception;
1083 }
1084 std::vector<T> ret;
1085 ret.reserve(r.size());
1086 for (auto const &row : r)
1087 {
1088 ret.emplace_back(row);
1089 }
1090 return ret;
1091}
1092
1093template <typename T>
1094inline void Mapper<T>::findBy(const Criteria &criteria,
1095 const MultipleRowsCallback &rcb,
1096 const ExceptionCallback &ecb) noexcept
1097{
1098 std::string sql = "select * from ";
1099 sql += T::tableName;
1100 sql += joinString_;
1101 bool hasParameters = false;
1102 if (criteria)
1103 {
1104 hasParameters = true;
1105 sql += " where ";
1106 sql += criteria.criteriaString();
1107 }
1108 sql.append(orderByString_);
1109 if (limit_ > 0)
1110 {
1111 hasParameters = true;
1112 sql.append(" limit $?");
1113 }
1114 if (offset_ > 0)
1115 {
1116 hasParameters = true;
1117 sql.append(" offset $?");
1118 }
1119 if (hasParameters)
1120 sql = replaceSqlPlaceHolder(sql, "$?");
1121 if (forUpdate_)
1122 {
1123 sql += " for update";
1124 }
1125 auto binder = *client_ << std::move(sql);
1126 if (criteria)
1127 criteria.outputArgs(binder);
1128 if (limit_ > 0)
1129 binder << limit_;
1130 if (offset_)
1131 binder << offset_;
1132 clear();
1133 binder >> [rcb](const Result &r) {
1134 std::vector<T> ret;
1135 ret.reserve(r.size());
1136 for (auto const &row : r)
1137 {
1138 ret.emplace_back(row);
1139 }
1140 rcb(std::move(ret));
1141 };
1142 binder >> ecb;
1143}
1144
1145template <typename T>
1146inline std::future<std::vector<T>> Mapper<T>::findFutureBy(
1147 const Criteria &criteria) noexcept
1148{
1149 std::string sql = "select * from ";
1150 sql += T::tableName;
1151 sql += joinString_;
1152 bool hasParameters = false;
1153 if (criteria)
1154 {
1155 hasParameters = true;
1156 sql += " where ";
1157 sql += criteria.criteriaString();
1158 }
1159 sql.append(orderByString_);
1160 if (limit_ > 0)
1161 {
1162 hasParameters = true;
1163 sql.append(" limit $?");
1164 }
1165 if (offset_ > 0)
1166 {
1167 hasParameters = true;
1168 sql.append(" offset $?");
1169 }
1170 if (hasParameters)
1171 sql = replaceSqlPlaceHolder(sql, "$?");
1172 if (forUpdate_)
1173 {
1174 sql += " for update";
1175 }
1176 auto binder = *client_ << std::move(sql);
1177 if (criteria)
1178 criteria.outputArgs(binder);
1179 if (limit_ > 0)
1180 binder << limit_;
1181 if (offset_)
1182 binder << offset_;
1183 clear();
1184 std::shared_ptr<std::promise<std::vector<T>>> prom =
1185 std::make_shared<std::promise<std::vector<T>>>();
1186 binder >> [prom](const Result &r) {
1187 std::vector<T> ret;
1188 ret.reserve(r.size());
1189 for (auto const &row : r)
1190 {
1191 ret.emplace_back(row);
1192 }
1193 prom->set_value(std::move(ret));
1194 };
1195 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1196 binder.exec();
1197 return prom->get_future();
1198}
1199
1200template <typename T>
1201inline std::vector<T> Mapper<T>::findAll() noexcept(false)
1202{
1203 return findBy(Criteria());
1204}
1205
1206template <typename T>
1207inline void Mapper<T>::findAll(const MultipleRowsCallback &rcb,
1208 const ExceptionCallback &ecb) noexcept
1209{
1210 findBy(Criteria(), rcb, ecb);
1211}
1212
1213template <typename T>
1214inline std::future<std::vector<T>> Mapper<T>::findFutureAll() noexcept
1215{
1216 return findFutureBy(Criteria());
1217}
1218
1219template <typename T>
1220inline size_t Mapper<T>::count(const Criteria &criteria) noexcept(false)
1221{
1222 std::string sql = "select count(*) from ";
1223 sql += T::tableName;
1224 sql += joinString_;
1225 if (criteria)
1226 {
1227 sql += " where ";
1228 sql += criteria.criteriaString();
1229 sql = replaceSqlPlaceHolder(sql, "$?");
1230 }
1231 clear();
1232 Result r(nullptr);
1233 {
1234 auto binder = *client_ << std::move(sql);
1235 if (criteria)
1236 criteria.outputArgs(binder);
1237 binder << Mode::Blocking;
1238 binder >> [&r](const Result &result) { r = result; };
1239 binder.exec(); // exec may be throw exception;
1240 }
1241 assert(r.size() == 1);
1242 return r[0][(Row::SizeType)0].as<size_t>();
1243}
1244
1245template <typename T>
1246inline void Mapper<T>::count(const Criteria &criteria,
1247 const CountCallback &rcb,
1248 const ExceptionCallback &ecb) noexcept
1249{
1250 std::string sql = "select count(*) from ";
1251 sql += T::tableName;
1252 sql += joinString_;
1253 if (criteria)
1254 {
1255 sql += " where ";
1256 sql += criteria.criteriaString();
1257 sql = replaceSqlPlaceHolder(sql, "$?");
1258 }
1259 clear();
1260 auto binder = *client_ << std::move(sql);
1261 if (criteria)
1262 criteria.outputArgs(binder);
1263 binder >> [rcb](const Result &r) {
1264 assert(r.size() == 1);
1265 rcb(r[0][(Row::SizeType)0].as<size_t>());
1266 };
1267 binder >> ecb;
1268}
1269
1270template <typename T>
1271inline std::future<size_t> Mapper<T>::countFuture(
1272 const Criteria &criteria) noexcept
1273{
1274 std::string sql = "select count(*) from ";
1275 sql += T::tableName;
1276 sql += joinString_;
1277 if (criteria)
1278 {
1279 sql += " where ";
1280 sql += criteria.criteriaString();
1281 sql = replaceSqlPlaceHolder(sql, "$?");
1282 }
1283 clear();
1284 auto binder = *client_ << std::move(sql);
1285 if (criteria)
1286 criteria.outputArgs(binder);
1287
1288 std::shared_ptr<std::promise<size_t>> prom =
1289 std::make_shared<std::promise<size_t>>();
1290 binder >> [prom](const Result &r) {
1291 assert(r.size() == 1);
1292 prom->set_value(r[0][(Row::SizeType)0].as<size_t>());
1293 };
1294 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1295 binder.exec();
1296 return prom->get_future();
1297}
1298
1299template <typename T>
1300inline void Mapper<T>::insert(T &obj) noexcept(false)
1301{
1302 clear();
1303 Result r(nullptr);
1304 bool needSelection = false;
1305 {
1306 auto binder = *client_ << obj.sqlForInserting(needSelection);
1307 obj.outputArgs(binder);
1308 binder << Mode::Blocking;
1309 binder >> [&r](const Result &result) { r = result; };
1310 binder.exec(); // Maybe throw exception;
1311 }
1312 assert(r.affectedRows() == 1);
1313 if (client_->type() == ClientType::PostgreSQL)
1314 {
1315 if (needSelection)
1316 {
1317 assert(r.size() == 1);
1318 obj = T(r[0]);
1319 }
1320 }
1321 else // Mysql or Sqlite3
1322 {
1323 auto id = r.insertId();
1324 obj.updateId(id);
1325 if (needSelection)
1326 {
1327 obj = findByPrimaryKey(obj.getPrimaryKey());
1328 }
1329 }
1330}
1331
1332template <typename T>
1333inline void Mapper<T>::insert(const T &obj,
1334 const SingleRowCallback &rcb,
1335 const ExceptionCallback &ecb) noexcept
1336{
1337 clear();
1338 bool needSelection = false;
1339 auto binder = *client_ << obj.sqlForInserting(needSelection);
1340 obj.outputArgs(binder);
1341 auto client = client_;
1342 binder >> [client, rcb, obj, needSelection, ecb](const Result &r) {
1343 assert(r.affectedRows() == 1);
1344 if (client->type() == ClientType::PostgreSQL)
1345 {
1346 if (needSelection)
1347 {
1348 assert(r.size() == 1);
1349 rcb(T(r[0]));
1350 }
1351 else
1352 {
1353 rcb(obj);
1354 }
1355 }
1356 else // Mysql or Sqlite3
1357 {
1358 auto id = r.insertId();
1359 auto newObj = obj;
1360 newObj.updateId(id);
1361 if (needSelection)
1362 {
1363 auto tmp = Mapper<T>(client);
1364 tmp.findByPrimaryKey(newObj.getPrimaryKey(), rcb, ecb);
1365 }
1366 else
1367 {
1368 rcb(newObj);
1369 }
1370 }
1371 };
1372 binder >> ecb;
1373}
1374
1375template <typename T>
1376inline std::future<T> Mapper<T>::insertFuture(const T &obj) noexcept
1377{
1378 clear();
1379 bool needSelection = false;
1380 auto binder = *client_ << obj.sqlForInserting(needSelection);
1381 obj.outputArgs(binder);
1382
1383 std::shared_ptr<std::promise<T>> prom = std::make_shared<std::promise<T>>();
1384 auto client = client_;
1385 binder >> [client, prom, obj, needSelection](const Result &r) {
1386 assert(r.affectedRows() == 1);
1387 if (client->type() == ClientType::PostgreSQL)
1388 {
1389 if (needSelection)
1390 {
1391 assert(r.size() == 1);
1392 prom->set_value(T(r[0]));
1393 }
1394 else
1395 {
1396 prom->set_value(obj);
1397 }
1398 }
1399 else // Mysql or Sqlite3
1400 {
1401 auto id = r.insertId();
1402 auto newObj = obj;
1403 newObj.updateId(id);
1404 if (needSelection)
1405 {
1406 auto tmp = Mapper<T>(client);
1407 tmp.findByPrimaryKey(
1408 newObj.getPrimaryKey(),
1409 [prom](T selObj) { prom->set_value(selObj); },
1410 [prom](const DrogonDbException &e) {
1411 prom->set_exception(
1412 std::make_exception_ptr(Failure(e.base().what())));
1413 });
1414 }
1415 else
1416 {
1417 prom->set_value(newObj);
1418 }
1419 }
1420 };
1421 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1422 binder.exec();
1423 return prom->get_future();
1424}
1425
1426template <typename T>
1427inline size_t Mapper<T>::update(const T &obj) noexcept(false)
1428{
1429 clear();
1430 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1431 "No primary key in the table!");
1432 std::vector<std::string> colNames = obj.updateColumns();
1433 if (colNames.empty())
1434 {
1435 return 0;
1436 }
1437 std::string sql = "update ";
1438 sql += T::tableName;
1439 sql += " set ";
1440 for (auto const &colName : colNames)
1441 {
1442 sql += colName;
1443 sql += " = $?,";
1444 }
1445 sql[sql.length() - 1] = ' '; // Replace the last ','
1446
1447 makePrimaryKeyCriteria(sql);
1448
1449 sql = replaceSqlPlaceHolder(sql, "$?");
1450 Result r(nullptr);
1451 {
1452 auto binder = *client_ << std::move(sql);
1453 obj.updateArgs(binder);
1454 outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);
1455 binder << Mode::Blocking;
1456 binder >> [&r](const Result &result) { r = result; };
1457 binder.exec(); // Maybe throw exception;
1458 }
1459 return r.affectedRows();
1460}
1461
1462template <typename T>
1463template <typename... Arguments>
1464size_t Mapper<T>::updateBy(const std::vector<std::string> &colNames,
1465 const Criteria &criteria,
1466 Arguments &&...args) noexcept(false)
1467{
1468 static_assert(sizeof...(args) > 0);
1469 assert(colNames.size() == sizeof...(args));
1470 clear();
1471 std::string sql = "update ";
1472 sql += T::tableName;
1473 sql += " set ";
1474 for (auto const &colName : colNames)
1475 {
1476 sql += colName;
1477 sql += " = $?,";
1478 }
1479 sql[sql.length() - 1] = ' '; // Replace the last ','
1480
1481 if (criteria)
1482 {
1483 sql += " where ";
1484 sql += criteria.criteriaString();
1485 }
1486
1487 sql = replaceSqlPlaceHolder(sql, "$?");
1488 Result r(nullptr);
1489 {
1490 auto binder = *client_ << std::move(sql);
1491 (void)std::initializer_list<int>{
1492 (binder << std::forward<Arguments>(args), 0)...};
1493 if (criteria)
1494 criteria.outputArgs(binder);
1495 binder << Mode::Blocking;
1496 binder >> [&r](const Result &result) { r = result; };
1497 binder.exec(); // Maybe throw exception;
1498 }
1499 return r.affectedRows();
1500}
1501
1502template <typename T>
1503inline void Mapper<T>::update(const T &obj,
1504 const CountCallback &rcb,
1505 const ExceptionCallback &ecb) noexcept
1506{
1507 clear();
1508 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1509 "No primary key in the table!");
1510
1511 std::vector<std::string> colNames = obj.updateColumns();
1512 if (colNames.empty())
1513 {
1514 rcb(0);
1515 return;
1516 }
1517 std::string sql = "update ";
1518 sql += T::tableName;
1519 sql += " set ";
1520 for (auto const &colName : colNames)
1521 {
1522 sql += colName;
1523 sql += " = $?,";
1524 }
1525 sql[sql.length() - 1] = ' '; // Replace the last ','
1526
1527 makePrimaryKeyCriteria(sql);
1528
1529 sql = replaceSqlPlaceHolder(sql, "$?");
1530 auto binder = *client_ << std::move(sql);
1531 obj.updateArgs(binder);
1532 outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);
1533 binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
1534 binder >> ecb;
1535}
1536
1537template <typename T>
1538template <typename... Arguments>
1539void Mapper<T>::updateBy(const std::vector<std::string> &colNames,
1540 const CountCallback &rcb,
1541 const ExceptionCallback &ecb,
1542 const Criteria &criteria,
1543 Arguments &&...args) noexcept
1544{
1545 static_assert(sizeof...(args) > 0);
1546 assert(colNames.size() == sizeof...(args));
1547 clear();
1548 std::string sql = "update ";
1549 sql += T::tableName;
1550 sql += " set ";
1551 for (auto const &colName : colNames)
1552 {
1553 sql += colName;
1554 sql += " = $?,";
1555 }
1556 sql[sql.length() - 1] = ' '; // Replace the last ','
1557
1558 if (criteria)
1559 {
1560 sql += " where ";
1561 sql += criteria.criteriaString();
1562 }
1563
1564 sql = replaceSqlPlaceHolder(sql, "$?");
1565 auto binder = *client_ << std::move(sql);
1566 (void)std::initializer_list<int>{
1567 (binder << std::forward<Arguments>(args), 0)...};
1568 if (criteria)
1569 criteria.outputArgs(binder);
1570 binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
1571 binder >> ecb;
1572}
1573
1574template <typename T>
1575inline std::future<size_t> Mapper<T>::updateFuture(const T &obj) noexcept
1576{
1577 clear();
1578 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1579 "No primary key in the table!");
1580 std::vector<std::string> colNames = obj.updateColumns();
1581 if (colNames.empty())
1582 {
1583 std::shared_ptr<std::promise<size_t>> prom =
1584 std::make_shared<std::promise<size_t>>();
1585 prom->set_value(0);
1586 return prom->get_future();
1587 }
1588 std::string sql = "update ";
1589 sql += T::tableName;
1590 sql += " set ";
1591 for (auto const &colName : colNames)
1592 {
1593 sql += colName;
1594 sql += " = $?,";
1595 }
1596 sql[sql.length() - 1] = ' '; // Replace the last ','
1597
1598 makePrimaryKeyCriteria(sql);
1599
1600 sql = replaceSqlPlaceHolder(sql, "$?");
1601 auto binder = *client_ << std::move(sql);
1602 obj.updateArgs(binder);
1603 outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);
1604
1605 std::shared_ptr<std::promise<size_t>> prom =
1606 std::make_shared<std::promise<size_t>>();
1607 binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
1608 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1609 binder.exec();
1610 return prom->get_future();
1611}
1612
1613template <typename T>
1614template <typename... Arguments>
1615inline std::future<size_t> Mapper<T>::updateFutureBy(
1616 const std::vector<std::string> &colNames,
1617 const Criteria &criteria,
1618 Arguments &&...args) noexcept
1619{
1620 static_assert(sizeof...(args) > 0);
1621 assert(colNames.size() == sizeof...(args));
1622 clear();
1623 std::string sql = "update ";
1624 sql += T::tableName;
1625 sql += " set ";
1626 for (auto const &colName : colNames)
1627 {
1628 sql += colName;
1629 sql += " = $?,";
1630 }
1631 sql[sql.length() - 1] = ' '; // Replace the last ','
1632
1633 if (criteria)
1634 {
1635 sql += " where ";
1636 sql += criteria.criteriaString();
1637 }
1638
1639 sql = replaceSqlPlaceHolder(sql, "$?");
1640 auto binder = *client_ << std::move(sql);
1641 (void)std::initializer_list<int>{
1642 (binder << std::forward<Arguments>(args), 0)...};
1643 if (criteria)
1644 criteria.outputArgs(binder);
1645
1646 std::shared_ptr<std::promise<size_t>> prom =
1647 std::make_shared<std::promise<size_t>>();
1648 binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
1649 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1650 binder.exec();
1651 return prom->get_future();
1652}
1653
1654template <typename T>
1655template <typename... Arguments>
1656inline size_t Mapper<T>::increment(const std::vector<std::string> &colNames,
1657 const Criteria &criteria,
1658 Arguments... args) noexcept(false)
1659{
1660 static_assert(sizeof...(args) > 0);
1661 assert(colNames.size() == sizeof...(args));
1662 clear();
1663 std::string sql = "update ";
1664 sql += T::tableName;
1665 sql += " set ";
1666
1667 std::vector<const char *> temps;
1668 (void)std::initializer_list<int>{(
1669 [&args, &temps] {
1670 args = (args < 0) ? (temps.push_back(" - $?,"), -args)
1671 : (temps.push_back(" + $?,"), args);
1672 }(),
1673 0)...};
1674
1675 for (int i = 0; i < sizeof...(args); ++i)
1676 {
1677 const auto &colName = colNames[i];
1678 sql += colName;
1679 sql += " = ";
1680 sql += colName;
1681 sql += temps[i];
1682 }
1683 sql[sql.length() - 1] = ' '; // Replace the last ','
1684
1685 if (criteria)
1686 {
1687 sql += " where ";
1688 sql += criteria.criteriaString();
1689 }
1690
1691 sql = replaceSqlPlaceHolder(sql, "$?");
1692 Result r(nullptr);
1693 {
1694 auto binder = *client_ << std::move(sql);
1695 (void)std::initializer_list<int>{
1696 (binder << std::forward<Arguments>(args), 0)...};
1697 if (criteria)
1698 criteria.outputArgs(binder);
1699 binder << Mode::Blocking;
1700 binder >> [&r](const Result &result) { r = result; };
1701 binder.exec(); // Maybe throw exception;
1702 }
1703 return r.affectedRows();
1704}
1705
1706template <typename T>
1707template <typename... Arguments>
1708inline void Mapper<T>::increment(const std::vector<std::string> &colNames,
1709 const CountCallback &rcb,
1710 const ExceptionCallback &ecb,
1711 const Criteria &criteria,
1712 Arguments... args) noexcept
1713{
1714 static_assert(sizeof...(args) > 0);
1715 assert(colNames.size() == sizeof...(args));
1716 clear();
1717 std::string sql = "update ";
1718 sql += T::tableName;
1719 sql += " set ";
1720
1721 std::vector<const char *> temps;
1722 (void)std::initializer_list<int>{(
1723 [&args, &temps] {
1724 args = args < 0 ? (temps.push_back(" - $?,"), -args)
1725 : (temps.push_back(" + $?,"), args);
1726 }(),
1727 0)...};
1728
1729 for (int i = 0; i < sizeof...(args); ++i)
1730 {
1731 const auto &colName = colNames[i];
1732 sql += colName;
1733 sql += " = ";
1734 sql += colName;
1735 sql += temps[i];
1736 }
1737 sql[sql.length() - 1] = ' '; // Replace the last ','
1738
1739 if (criteria)
1740 {
1741 sql += " where ";
1742 sql += criteria.criteriaString();
1743 }
1744
1745 sql = replaceSqlPlaceHolder(sql, "$?");
1746 auto binder = *client_ << std::move(sql);
1747 (void)std::initializer_list<int>{
1748 (binder << std::forward<Arguments>(args), 0)...};
1749 if (criteria)
1750 criteria.outputArgs(binder);
1751 binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
1752 binder >> ecb;
1753}
1754
1755template <typename T>
1756template <typename... Arguments>
1757inline std::future<size_t> Mapper<T>::incrementFuture(
1758 const std::vector<std::string> &colNames,
1759 const Criteria &criteria,
1760 Arguments... args) noexcept
1761{
1762 static_assert(sizeof...(args) > 0);
1763 assert(colNames.size() == sizeof...(args));
1764 clear();
1765 std::string sql = "update ";
1766 sql += T::tableName;
1767 sql += " set ";
1768
1769 std::vector<const char *> temps;
1770 (void)std::initializer_list<int>{(
1771 [&args, &temps] {
1772 args = args < 0 ? (temps.push_back(" - $?,"), -args)
1773 : (temps.push_back(" + $?,"), args);
1774 }(),
1775 0)...};
1776
1777 for (int i = 0; i < sizeof...(args); ++i)
1778 {
1779 const auto &colName = colNames[i];
1780 sql += colName;
1781 sql += " = ";
1782 sql += colName;
1783 sql += temps[i];
1784 }
1785 sql[sql.length() - 1] = ' '; // Replace the last ','
1786
1787 if (criteria)
1788 {
1789 sql += " where ";
1790 sql += criteria.criteriaString();
1791 }
1792
1793 sql = replaceSqlPlaceHolder(sql, "$?");
1794 auto binder = *client_ << std::move(sql);
1795 (void)std::initializer_list<int>{
1796 (binder << std::forward<Arguments>(args), 0)...};
1797 if (criteria)
1798 criteria.outputArgs(binder);
1799
1800 std::shared_ptr<std::promise<size_t>> prom =
1801 std::make_shared<std::promise<size_t>>();
1802 binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
1803 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1804 binder.exec();
1805 return prom->get_future();
1806}
1807
1808template <typename T>
1809inline size_t Mapper<T>::deleteOne(const T &obj) noexcept(false)
1810{
1811 clear();
1812 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1813 "No primary key in the table!");
1814 std::string sql = "delete from ";
1815 sql += T::tableName;
1816
1817 sql += " "; // Replace the last ','
1818
1819 makePrimaryKeyCriteria(sql);
1820
1821 sql = replaceSqlPlaceHolder(sql, "$?");
1822 Result r(nullptr);
1823 {
1824 auto binder = *client_ << std::move(sql);
1825 outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);
1826 binder << Mode::Blocking;
1827 binder >> [&r](const Result &result) { r = result; };
1828 binder.exec(); // Maybe throw exception;
1829 }
1830 return r.affectedRows();
1831}
1832
1833template <typename T>
1834inline void Mapper<T>::deleteOne(const T &obj,
1835 const CountCallback &rcb,
1836 const ExceptionCallback &ecb) noexcept
1837{
1838 clear();
1839 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1840 "No primary key in the table!");
1841 std::string sql = "delete from ";
1842 sql += T::tableName;
1843 sql += " ";
1844
1845 makePrimaryKeyCriteria(sql);
1846
1847 sql = replaceSqlPlaceHolder(sql, "$?");
1848 auto binder = *client_ << std::move(sql);
1849 outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);
1850 binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
1851 binder >> ecb;
1852}
1853
1854template <typename T>
1855inline std::future<size_t> Mapper<T>::deleteFutureOne(const T &obj) noexcept
1856{
1857 clear();
1858 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1859 "No primary key in the table!");
1860 std::string sql = "delete from ";
1861 sql += T::tableName;
1862 sql += " ";
1863
1864 makePrimaryKeyCriteria(sql);
1865
1866 sql = replaceSqlPlaceHolder(sql, "$?");
1867 auto binder = *client_ << std::move(sql);
1868 outputPrimaryKeyToBinder(obj.getPrimaryKey(), binder);
1869
1870 std::shared_ptr<std::promise<size_t>> prom =
1871 std::make_shared<std::promise<size_t>>();
1872 binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
1873 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1874 binder.exec();
1875 return prom->get_future();
1876}
1877
1878template <typename T>
1879inline size_t Mapper<T>::deleteBy(const Criteria &criteria) noexcept(false)
1880{
1881 clear();
1882 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1883 "No primary key in the table!");
1884 std::string sql = "delete from ";
1885 sql += T::tableName;
1886
1887 if (criteria)
1888 {
1889 sql += " where ";
1890 sql += criteria.criteriaString();
1891 sql = replaceSqlPlaceHolder(sql, "$?");
1892 }
1893
1894 Result r(nullptr);
1895 {
1896 auto binder = *client_ << std::move(sql);
1897 if (criteria)
1898 {
1899 criteria.outputArgs(binder);
1900 }
1901 binder << Mode::Blocking;
1902 binder >> [&r](const Result &result) { r = result; };
1903 binder.exec(); // Maybe throw exception;
1904 }
1905 return r.affectedRows();
1906}
1907
1908template <typename T>
1909inline void Mapper<T>::deleteBy(const Criteria &criteria,
1910 const CountCallback &rcb,
1911 const ExceptionCallback &ecb) noexcept
1912{
1913 clear();
1914 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1915 "No primary key in the table!");
1916 std::string sql = "delete from ";
1917 sql += T::tableName;
1918
1919 if (criteria)
1920 {
1921 sql += " where ";
1922 sql += criteria.criteriaString();
1923 sql = replaceSqlPlaceHolder(sql, "$?");
1924 }
1925
1926 auto binder = *client_ << std::move(sql);
1927 if (criteria)
1928 {
1929 criteria.outputArgs(binder);
1930 }
1931 binder >> [rcb](const Result &r) { rcb(r.affectedRows()); };
1932 binder >> ecb;
1933}
1934
1935template <typename T>
1936inline std::future<size_t> Mapper<T>::deleteFutureBy(
1937 const Criteria &criteria) noexcept
1938{
1939 clear();
1940 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
1941 "No primary key in the table!");
1942 std::string sql = "delete from ";
1943 sql += T::tableName;
1944 if (criteria)
1945 {
1946 sql += " where ";
1947 sql += criteria.criteriaString();
1948 sql = replaceSqlPlaceHolder(sql, "$?");
1949 }
1950 auto binder = *client_ << std::move(sql);
1951 if (criteria)
1952 {
1953 criteria.outputArgs(binder);
1954 }
1955
1956 std::shared_ptr<std::promise<size_t>> prom =
1957 std::make_shared<std::promise<size_t>>();
1958 binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
1959 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
1960 binder.exec();
1961 return prom->get_future();
1962}
1963
1964template <typename T>
1966{
1967 assert(limit > 0);
1968 limit_ = limit;
1969 return *this;
1970}
1971
1972template <typename T>
1974{
1975 offset_ = offset;
1976 return *this;
1977}
1978
1979template <typename T>
1980inline Mapper<T> &Mapper<T>::orderBy(const std::string &colName,
1981 const SortOrder &order)
1982{
1983 if (orderByString_.empty())
1984 {
1985 orderByString_ =
1986 utils::formattedString(" order by %s", colName.c_str());
1987 if (order == SortOrder::DESC)
1988 {
1989 orderByString_ += " desc";
1990 }
1991 }
1992 else
1993 {
1994 orderByString_ += ",";
1995 orderByString_ += colName;
1996 if (order == SortOrder::DESC)
1997 {
1998 orderByString_ += " desc";
1999 }
2000 }
2001 return *this;
2002}
2003
2004template <typename T>
2005inline Mapper<T> &Mapper<T>::orderBy(size_t colIndex, const SortOrder &order)
2006{
2007 std::string colName = T::getColumnName(colIndex);
2008 assert(!colName.empty());
2009 return orderBy(colName, order);
2010}
2011
2012template <typename T>
2013inline Mapper<T> &Mapper<T>::paginate(size_t page, size_t perPage)
2014{
2015 assert(page > 0 && perPage > 0);
2016 return limit(perPage).offset((page - 1) * perPage);
2017}
2018
2019template <typename T>
2021{
2022 forUpdate_ = true;
2023 return *this;
2024}
2025
2026template <typename T>
2027inline std::string Mapper<T>::replaceSqlPlaceHolder(
2028 const std::string &sqlStr,
2029 const std::string &holderStr) const
2030{
2031 if (client_->type() == ClientType::PostgreSQL)
2032 {
2033 std::string::size_type startPos = 0;
2034 std::string::size_type pos;
2035 std::stringstream ret;
2036 size_t phCount = 1;
2037 do
2038 {
2039 pos = sqlStr.find(holderStr, startPos);
2040 if (pos == std::string::npos)
2041 {
2042 ret << sqlStr.substr(startPos);
2043 return ret.str();
2044 }
2045 ret << sqlStr.substr(startPos, pos - startPos);
2046 ret << "$";
2047 ret << phCount++;
2048 startPos = pos + holderStr.length();
2049 } while (1);
2050 }
2051 else if (client_->type() == ClientType::Mysql ||
2052 client_->type() == ClientType::Sqlite3)
2053 {
2054 std::string::size_type startPos = 0;
2055 std::string::size_type pos;
2056 std::stringstream ret;
2057 do
2058 {
2059 pos = sqlStr.find(holderStr, startPos);
2060 if (pos == std::string::npos)
2061 {
2062 ret << sqlStr.substr(startPos);
2063 return ret.str();
2064 }
2065 ret << sqlStr.substr(startPos, pos - startPos);
2066 ret << "?";
2067 startPos = pos + holderStr.length();
2068 } while (1);
2069 }
2070 else
2071 {
2072 return sqlStr;
2073 }
2074}
2075
2076template <typename T>
2077inline size_t Mapper<T>::deleteByPrimaryKey(
2078 const typename Mapper<T>::TraitsPKType &key) noexcept(false)
2079{
2080 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
2081 "No primary key in the table!");
2082 static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,
2083 "No function member named sqlForDeletingByPrimaryKey, please "
2084 "make sure that the model class is generated by the latest "
2085 "version of drogon_ctl");
2086 clear();
2087 Result r(nullptr);
2088 {
2089 auto binder = *client_ << T::sqlForDeletingByPrimaryKey();
2090 outputPrimaryKeyToBinder(key, binder);
2091 binder << Mode::Blocking;
2092 binder >> [&r](const Result &result) { r = result; };
2093 binder.exec(); // exec may be throw exception;
2094 }
2095 return r.affectedRows();
2096}
2097
2098template <typename T>
2100 const typename Mapper<T>::TraitsPKType &key,
2101 const CountCallback &rcb,
2102 const ExceptionCallback &ecb) noexcept
2103{
2104 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
2105 "No primary key in the table!");
2106 static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,
2107 "No function member named sqlForDeletingByPrimaryKey, please "
2108 "make sure that the model class is generated by the latest "
2109 "version of drogon_ctl");
2110 clear();
2111 auto binder = *client_ << T::sqlForDeletingByPrimaryKey();
2112 outputPrimaryKeyToBinder(key, binder);
2113 binder >>
2114 [rcb = std::move(rcb)](const Result &r) { rcb(r.affectedRows()); };
2115 binder >> ecb;
2116}
2117
2118template <typename T>
2119inline std::future<size_t> Mapper<T>::deleteFutureByPrimaryKey(
2120 const typename Mapper<T>::TraitsPKType &key) noexcept
2121{
2122 static_assert(!std::is_same_v<typename T::PrimaryKeyType, void>,
2123 "No primary key in the table!");
2124 static_assert(internal::has_sqlForDeletingByPrimaryKey<T>::value,
2125 "No function member named sqlForDeletingByPrimaryKey, please "
2126 "make sure that the model class is generated by the latest "
2127 "version of drogon_ctl");
2128 clear();
2129 auto binder = *client_ << T::sqlForDeletingByPrimaryKey();
2130 outputPrimaryKeyToBinder(key, binder);
2131
2132 std::shared_ptr<std::promise<size_t>> prom =
2133 std::make_shared<std::promise<size_t>>();
2134 binder >> [prom](const Result &r) { prom->set_value(r.affectedRows()); };
2135 binder >> [prom](const std::exception_ptr &e) { prom->set_exception(e); };
2136 binder.exec();
2137 return prom->get_future();
2138}
2139
2146template <typename Value>
2147inline Json::Value toJson(const std::vector<Value> &container)
2148{
2149 Json::Value values(Json::arrayValue);
2150 for (const Value &c : container)
2151 {
2152 values.append(c.toJson());
2153 }
2154 return values;
2155}
2156
2157} // namespace orm
2158} // namespace drogon
bool isValidSqlIdentifier(const std::string &identifier)
Validate that a string is a safe SQL identifier.
Definition BaseBuilder.h:116
Json::Value toJson(const std::vector< Value > &container)
Convert std::vector<Value> where Value can be JSON to JSON.
Definition Mapper.h:2147
DROGON_EXPORT std::string formattedString(const char *format,...)
Get a formatted string.
this class represents a comparison condition.
Definition Criteria.h:74
Mixin base class to identify drogon-db-specific exception types.
Definition Exception.h:51
The mapper template.
Definition Mapper.h:117
std::future< size_t > deleteFutureBy(const Criteria &criteria) noexcept
Delete records that match the given criteria asynchronously.
Definition Mapper.h:1936
Mapper< T > & rightJoin(const std::string &table, const std::string &onLeft, const std::string &onRight)
Add a RIGHT JOIN clause to the query.
Definition Mapper.h:238
std::vector< T > findBy(const Criteria &criteria) noexcept(false)
Select the rows that match the given criteria.
Definition Mapper.h:1040
Mapper< T > & paginate(size_t page, size_t perPage)
Set limit and offset to achieve pagination. This method will override limit() and offset(),...
Definition Mapper.h:2013
void findByPrimaryKey(const TraitsPKType &key, const SingleRowCallback &rcb, const ExceptionCallback &ecb) noexcept
Asynchronously find a record by the primary key.
Definition Mapper.h:322
size_t updateBy(const std::vector< std::string > &colNames, const Criteria &criteria, Arguments &&...args) noexcept(false)
Update a record that match both the primary key and the given criteria.
Definition Mapper.h:1464
size_t deleteBy(const Criteria &criteria) noexcept(false)
Delete records that satisfy the given criteria.
Definition Mapper.h:1879
std::future< std::vector< T > > findFutureAll() noexcept
Asynchronously find all the records in the table.
Definition Mapper.h:1214
Mapper< T > & offset(size_t offset)
Add a offset to the query.
Definition Mapper.h:1973
Mapper< T > & limit(size_t limit)
Add a limit to the query.
Definition Mapper.h:1965
std::future< size_t > deleteFutureOne(const T &obj) noexcept
Asynchronously delete a record from the table.
Definition Mapper.h:1855
T findOne(const Criteria &criteria) noexcept(false)
Find one record that matches the given criteria.
Definition Mapper.h:866
std::future< size_t > incrementFuture(const std::vector< std::string > &colNames, const Criteria &criteria, Arguments... args) noexcept
Asynchronously update some records that match the given criteria.
Definition Mapper.h:1757
size_t deleteOne(const T &obj) noexcept(false)
Delete a record from the table.
Definition Mapper.h:1809
Mapper< T > & orderBy(const std::string &colName, const SortOrder &order=SortOrder::ASC)
Set the order of the results.
Definition Mapper.h:1980
std::vector< T > findAll() noexcept(false)
Find all the records in the table.
Definition Mapper.h:1201
std::future< std::vector< T > > findFutureBy(const Criteria &criteria) noexcept
Asynchronously select the rows that match the given criteria.
Definition Mapper.h:1146
size_t count(const Criteria &criteria=Criteria()) noexcept(false)
Get the count of rows that match the given criteria.
Definition Mapper.h:1220
size_t deleteByPrimaryKey(const TraitsPKType &key) noexcept(false)
Delete the record that matches the given primary key.
std::future< size_t > updateFuture(const T &obj) noexcept
Asynchronously update a record.
Definition Mapper.h:1575
std::future< T > findFutureOne(const Criteria &criteria) noexcept
Asynchronously find one record that matches the given criteria.
Definition Mapper.h:979
Mapper< T > & leftJoin(const std::string &table, const std::string &onLeft, const std::string &onRight)
Add a LEFT JOIN clause to the query.
Definition Mapper.h:214
size_t increment(const std::vector< std::string > &colNames, const Criteria &criteria, Arguments... args) noexcept(false)
Update some records that match the given criteria.
Definition Mapper.h:1656
Mapper(DbClientPtr client)
Construct a new Mapper object.
Definition Mapper.h:124
std::future< T > findFutureByPrimaryKey(const TraitsPKType &key) noexcept
Asynchronously find a record by the primary key.
Definition Mapper.h:377
size_t update(const T &obj) noexcept(false)
Update a record.
Definition Mapper.h:1427
std::future< size_t > updateFutureBy(const std::vector< std::string > &colNames, const Criteria &criteria, Arguments &&...args) noexcept
Asynchronously update a record that match both the primary key and the given criteria.
Definition Mapper.h:1615
void insert(T &obj) noexcept(false)
Insert a row into the table.
Definition Mapper.h:1300
Mapper< T > & forUpdate()
Lock the result for updating.
Definition Mapper.h:2020
T findByPrimaryKey(const TraitsPKType &key) noexcept(false)
Find a record by the primary key.
Definition Mapper.h:270
std::future< size_t > deleteFutureByPrimaryKey(const TraitsPKType &key) noexcept
Delete the record that matches the given primary key asynchronously.
Definition Mapper.h:2119
std::future< size_t > countFuture(const Criteria &criteria=Criteria()) noexcept
Asynchronously get the number of rows that match the given criteria.
Definition Mapper.h:1271
std::future< T > insertFuture(const T &) noexcept
Asynchronously insert a row into the table.
Definition Mapper.h:1376
Mapper< T > & innerJoin(const std::string &table, const std::string &onLeft, const std::string &onRight)
Add an INNER JOIN clause to the query.
Definition Mapper.h:190
Result set containing data returned by a query or command.
Definition Result.h:58
unsigned long long insertId() const noexcept
SizeType affectedRows() const noexcept
Query returned an unexpected number of rows.
Definition Exception.h:283
Drogon Test is a minimal effort test framework developed because the major C++ test frameworks doesn'...
Definition Attribute.h:23
STL namespace.
Definition Mapper.h:42