drogon
C++14/17-based HTTP application framework
Loading...
Searching...
No Matches
SqlBinder.h
Go to the documentation of this file.
1
14
15#pragma once
16#include <drogon/exports.h>
17#include <drogon/orm/DbTypes.h>
19#include <drogon/orm/Field.h>
20#include <drogon/orm/FunctionTraits.h>
21#include <drogon/orm/ResultIterator.h>
22#include <drogon/orm/Row.h>
23#include <drogon/orm/RowIterator.h>
24#include <string_view>
25#include <json/writer.h>
26#include <trantor/utils/Logger.h>
27#include <trantor/utils/NonCopyable.h>
28#include <json/json.h>
29#include <functional>
30#include <iostream>
31#include <map>
32#include <memory>
33#include <mutex>
34#include <sstream>
35#include <string.h>
36#include <string>
37#include <vector>
38#include <optional>
39#include <type_traits>
40#ifdef _WIN32
41#include <winsock2.h>
42#else // some Unix-like OS
43#include <arpa/inet.h>
44#endif
45
46#if defined __linux__ || defined __FreeBSD__ || defined __OpenBSD__ || \
47 defined __MINGW32__ || defined __HAIKU__
48
49#ifdef __linux__
50#include <endian.h> // __BYTE_ORDER __LITTLE_ENDIAN
51#elif defined __FreeBSD__ || defined __OpenBSD__
52#include <sys/endian.h> // _BYTE_ORDER _LITTLE_ENDIAN
53#define __BYTE_ORDER _BYTE_ORDER
54#define __LITTLE_ENDIAN _LITTLE_ENDIAN
55#elif defined __MINGW32__
56#include <sys/param.h> // BYTE_ORDER LITTLE_ENDIAN
57#define __BYTE_ORDER BYTE_ORDER
58#define __LITTLE_ENDIAN LITTLE_ENDIAN
59#endif
60
61#include <algorithm> // std::reverse()
62
63template <typename T>
64constexpr T htonT(T value) noexcept
65{
66#if __BYTE_ORDER == __LITTLE_ENDIAN
67 return (std::reverse(reinterpret_cast<char *>(&value),
68 reinterpret_cast<char *>(&value) + sizeof(T)),
69 value);
70#else
71 return value;
72#endif
73}
74
75#if (!defined _WIN32) || (defined _WIN32 && _WIN32_WINNT < _WIN32_WINNT_WIN8)
76inline uint64_t htonll(uint64_t value)
77{
78 return htonT<uint64_t>(value);
79}
80
81inline uint64_t ntohll(uint64_t value)
82{
83 return htonll(value);
84}
85#endif
86#endif
87
88namespace drogon
89{
90namespace orm
91{
92enum class ClientType
93{
94 PostgreSQL = 0,
95 Mysql,
96 Sqlite3
97};
98
99enum Sqlite3Type
100{
101 Sqlite3TypeChar = 0,
102 Sqlite3TypeShort,
103 Sqlite3TypeInt,
104 Sqlite3TypeInt64,
105 Sqlite3TypeDouble,
106 Sqlite3TypeText,
107 Sqlite3TypeBlob,
108 Sqlite3TypeNull
109};
110
111class DbClient;
112using QueryCallback = std::function<void(const Result &)>;
113using ExceptPtrCallback = std::function<void(const std::exception_ptr &)>;
114enum class Mode
115{
116 NonBlocking,
117 Blocking
118};
119
121{
122 std::shared_ptr<void> obj;
123 const char *parameter;
124 int length;
125 int format;
126};
127
128namespace internal
129{
130template <typename T>
132{
133 static const bool isVector = false;
134 static const bool isPtrVector = false;
135 using ItemsType = T;
136};
137
138template <typename T>
139struct VectorTypeTraits<std::vector<std::shared_ptr<T>>>
140{
141 static const bool isVector = true;
142 static const bool isPtrVector = true;
143 using ItemsType = T;
144};
145
146template <>
147struct VectorTypeTraits<std::string>
148{
149 static const bool isVector = false;
150 static const bool isPtrVector = false;
151 using ItemsType = std::string;
152};
153
154// we only accept value type or const lreference type or rreference type as
155// handle method parameters type
156template <typename T>
158{
159 static const bool isValid = true;
160};
161
162template <typename T>
164{
165 static const bool isValid = false;
166};
167
168template <typename T>
170{
171 static const bool isValid = false;
172};
173
174template <typename T>
176{
177 static const bool isValid = true;
178};
179
180template <typename T>
181struct CallbackArgTypeTraits<const T &>
182{
183 static const bool isValid = true;
184};
185
187{
188 public:
189 virtual ~CallbackHolderBase() = default;
190 virtual void execCallback(const Result &result) = 0;
191};
192
193template <typename Function>
194class CallbackHolder : public CallbackHolderBase
195{
196 public:
197 void execCallback(const Result &result) override
198 {
199 run(result);
200 }
201
202 template <typename T>
203 explicit CallbackHolder(T &&function) : function_(std::forward<T>(function))
204 {
205 static_assert(traits::isSqlCallback,
206 "Your sql callback function type is wrong!");
207 }
208
209 private:
210 Function function_;
211 using traits = FunctionTraits<Function>;
212 template <std::size_t Index>
213 using NthArgumentType = typename traits::template argument<Index>;
214 static const size_t argumentCount = traits::arity;
215
216 template <bool isStep = traits::isStepResultCallback>
217 void run(const Result &result)
218 {
219 if constexpr (isStep)
220 {
221 if (result.empty())
222 {
223 run(nullptr, true);
224 return;
225 }
226 for (auto const &row : result)
227 {
228 run(&row, false);
229 }
230 run(nullptr, true);
231 }
232 else
233 {
234 static_assert(argumentCount == 0,
235 "Your sql callback function type is wrong!");
236 function_(result);
237 }
238 }
239
240 template <typename... Values, std::size_t Boundary = argumentCount>
241 void run(const Row *const row, bool isNull, Values &&...values)
242 {
243 if constexpr (sizeof...(Values) < Boundary)
244 {
245 // call this function recursively until parameter's count equals to
246 // the count of target function parameters
247 static_assert(
249 NthArgumentType<sizeof...(Values)>>::isValid,
250 "your sql callback function argument type must be value "
251 "type or "
252 "const "
253 "left-reference type");
254 using ValueType =
255 typename std::remove_cv<typename std::remove_reference<
256 NthArgumentType<sizeof...(Values)>>::type>::type;
257 ValueType value = ValueType();
258 if (row && row->size() > sizeof...(Values))
259 {
260 // if(!VectorTypeTraits<ValueType>::isVector)
261 // value = (*row)[sizeof...(Values)].as<ValueType>();
262 // else
263 // ; // value =
264 // (*row)[sizeof...(Values)].asArray<VectorTypeTraits<ValueType>::ItemsType>();
265 value = makeValue<ValueType>(
266 (*row)[(Row::SizeType)sizeof...(Values)]);
267 }
268
269 run(row, isNull, std::forward<Values>(values)..., std::move(value));
270 }
271 else if constexpr (sizeof...(Values) == Boundary)
272 {
273 function_(isNull, std::move(values)...);
274 }
275 }
276
277 template <typename ValueType>
278 ValueType makeValue(const Field &field)
279 {
280 if constexpr (VectorTypeTraits<ValueType>::isVector)
281 {
282 return field
283 .asArray<typename VectorTypeTraits<ValueType>::ItemsType>();
284 }
285 else
286 {
287 return field.as<ValueType>();
288 }
289 }
290};
291
292class DROGON_EXPORT SqlBinder : public trantor::NonCopyable
293{
294 using self = SqlBinder;
295
296 public:
297 SqlBinder(const std::string &sql, DbClient &client, ClientType type)
298 : sqlPtr_(std::make_shared<std::string>(sql)),
299 sqlViewPtr_(sqlPtr_->data()),
300 sqlViewLength_(sqlPtr_->length()),
301 client_(client),
302 type_(type)
303 {
304 }
305
306 SqlBinder(std::string &&sql, DbClient &client, ClientType type)
307 : sqlPtr_(std::make_shared<std::string>(std::move(sql))),
308 sqlViewPtr_(sqlPtr_->data()),
309 sqlViewLength_(sqlPtr_->length()),
310 client_(client),
311 type_(type)
312 {
313 }
314
315 SqlBinder(const char *sql,
316 size_t sqlLength,
317 DbClient &client,
318 ClientType type)
319 : sqlViewPtr_(sql),
320 sqlViewLength_(sqlLength),
321 client_(client),
322 type_(type)
323 {
324 }
325
326 SqlBinder(SqlBinder &&that) noexcept
327 : sqlPtr_(std::move(that.sqlPtr_)),
328 sqlViewPtr_(that.sqlViewPtr_),
329 sqlViewLength_(that.sqlViewLength_),
330 client_(that.client_),
331 parametersNumber_(that.parametersNumber_),
332 parameters_(std::move(that.parameters_)),
333 lengths_(std::move(that.lengths_)),
334 formats_(std::move(that.formats_)),
335 objs_(std::move(that.objs_)),
336 mode_(that.mode_),
337 callbackHolder_(std::move(that.callbackHolder_)),
338 exceptionCallback_(std::move(that.exceptionCallback_)),
339 exceptionPtrCallback_(std::move(that.exceptionPtrCallback_)),
340 execed_(that.execed_),
341 destructed_(that.destructed_),
342 isExceptionPtr_(that.isExceptionPtr_),
343 type_(that.type_)
344 {
345 // set the execed_ to true to avoid the same sql being executed twice.
346 that.execed_ = true;
347 }
348
349 SqlBinder &operator=(SqlBinder &&that) = delete;
350 ~SqlBinder();
351
352 template <typename CallbackType,
353 typename traits =
355 self &operator>>(CallbackType &&callback)
356 {
357 if constexpr (traits::isExceptCallback)
358 {
359 if constexpr (traits::isPtr)
360 {
361 // LOG_DEBUG << "ptr callback";
362 isExceptionPtr_ = true;
363 exceptionPtrCallback_ = std::forward<CallbackType>(callback);
364 return *this;
365 }
366 else
367 {
368 isExceptionPtr_ = false;
369 exceptionCallback_ = std::forward<CallbackType>(callback);
370 return *this;
371 }
372 }
373 else if constexpr (traits::isSqlCallback)
374 {
375 callbackHolder_ = std::shared_ptr<CallbackHolderBase>(
376 new CallbackHolder<typename std::decay<CallbackType>::type>(
377 std::forward<CallbackType>(callback)));
378 return *this;
379 }
380 }
381
382 template <typename T>
383 self &operator<<(T &&parameter)
384 {
385 using ParaType = std::remove_cv_t<std::remove_reference_t<T>>;
386 ++parametersNumber_;
387 std::shared_ptr<void> obj = std::make_shared<ParaType>(parameter);
388 if (type_ == ClientType::PostgreSQL)
389 {
390 switch (sizeof(T))
391 {
392 case 2:
393 *std::static_pointer_cast<uint16_t>(obj) =
394 htons((uint16_t)parameter);
395 break;
396 case 4:
397 *std::static_pointer_cast<uint32_t>(obj) =
398 htonl((uint32_t)parameter);
399 break;
400 case 8:
401 *std::static_pointer_cast<uint64_t>(obj) =
402 htonll((uint64_t)parameter);
403 break;
404 case 1:
405 default:
406 break;
407 }
408 objs_.push_back(obj);
409 parameters_.push_back((char *)obj.get());
410 lengths_.push_back(sizeof(T));
411 formats_.push_back(1);
412 }
413 else if (type_ == ClientType::Mysql)
414 {
415 objs_.push_back(obj);
416 parameters_.push_back((char *)obj.get());
417 lengths_.push_back(0);
418 formats_.push_back(getMysqlType<ParaType>());
419 }
420 else if (type_ == ClientType::Sqlite3)
421 {
422 objs_.push_back(obj);
423 parameters_.push_back((char *)obj.get());
424 lengths_.push_back(0);
425 switch (sizeof(T))
426 {
427 case 1:
428 formats_.push_back(Sqlite3TypeChar);
429 break;
430 case 2:
431 formats_.push_back(Sqlite3TypeShort);
432 break;
433 case 4:
434 formats_.push_back(Sqlite3TypeInt);
435 break;
436 case 8:
437 formats_.push_back(Sqlite3TypeInt64);
438 default:
439 break;
440 }
441 }
442 // LOG_TRACE << "Bind parameter:" << parameter;
443 return *this;
444 }
445
446 self &operator<<(const RawParameter &);
447
448 self &operator<<(RawParameter &param)
449 {
450 return operator<<((const RawParameter &)param);
451 }
452
453 self &operator<<(RawParameter &&);
454
455 // template <>
456 self &operator<<(const char str[])
457 {
458 return operator<<(std::string(str));
459 }
460
461 self &operator<<(char str[])
462 {
463 return operator<<(std::string(str));
464 }
465
466 self &operator<<(const std::string_view &str);
467
468 self &operator<<(std::string_view &&str)
469 {
470 return operator<<((const std::string_view &)str);
471 }
472
473 self &operator<<(std::string_view &str)
474 {
475 return operator<<((const std::string_view &)str);
476 }
477
478 self &operator<<(const std::string &str);
479
480 self &operator<<(std::string &str)
481 {
482 return operator<<((const std::string &)str);
483 }
484
485 self &operator<<(std::string &&str);
486
487 self &operator<<(trantor::Date date)
488 {
489 return operator<<(date.toDbStringLocal());
490 }
491
492 self &operator<<(const std::vector<char> &v);
493
494 self &operator<<(std::vector<char> &v)
495 {
496 return operator<<((const std::vector<char> &)v);
497 }
498
499 self &operator<<(std::vector<char> &&v);
500
501 self &operator<<(float f)
502 {
503 if (type_ == ClientType::Sqlite3)
504 {
505 return operator<<((double)f);
506 }
507 return operator<<(std::to_string(f));
508 }
509
510 self &operator<<(double f);
511 self &operator<<(std::nullptr_t);
512 self &operator<<(DefaultValue dv);
513
514 self &operator<<(const Mode &mode)
515 {
516 mode_ = mode;
517 return *this;
518 }
519
520 self &operator<<(Mode &mode)
521 {
522 mode_ = mode;
523 return *this;
524 }
525
526 self &operator<<(Mode &&mode)
527 {
528 mode_ = mode;
529 return *this;
530 }
531
532 template <typename T>
533 self &operator<<(const std::optional<T> &parameter)
534 {
535 if (parameter)
536 {
537 return *this << parameter.value();
538 }
539 return *this << nullptr;
540 }
541
542 template <typename T>
543 self &operator<<(std::optional<T> &parameter)
544 {
545 if (parameter)
546 {
547 return *this << parameter.value();
548 }
549 return *this << nullptr;
550 }
551
552 template <typename T>
553 self &operator<<(std::optional<T> &&parameter)
554 {
555 if (parameter)
556 {
557 return *this << std::move(parameter.value());
558 }
559 return *this << nullptr;
560 }
561
562 self &operator<<(const Json::Value &j) noexcept(true)
563 {
564 switch (j.type())
565 {
566 case Json::nullValue:
567 return *this << nullptr;
568 case Json::intValue:
569 return *this << j.asInt64();
570 case Json::uintValue:
571 return *this << j.asUInt64();
572 case Json::realValue:
573 return *this << j.asDouble();
574 case Json::stringValue:
575 return *this << j.asString();
576 case Json::booleanValue:
577 return *this << j.asBool();
578 case Json::arrayValue:
579 case Json::objectValue:
580 default:
581 static Json::StreamWriterBuilder jsonBuilder;
582 std::once_flag once_json;
583 std::call_once(once_json,
584 []() { jsonBuilder["indentation"] = ""; });
585 return *this << Json::writeString(jsonBuilder, j);
586 }
587 }
588
589 self &operator<<(Json::Value &j) noexcept(true)
590 {
591 return *this << static_cast<const Json::Value &>(j);
592 }
593
594 self &operator<<(Json::Value &&j) noexcept(true)
595 {
596 return *this << static_cast<const Json::Value &>(j);
597 }
598
599 void exec() noexcept(false);
600
601 private:
602 static int getMysqlTypeBySize(size_t size);
603
604 template <typename T>
605 static int getMysqlType()
606 {
607 if constexpr (std::is_same_v<T, bool>)
608 {
609 return MySqlTiny;
610 }
611 else if constexpr (std::is_same_v<T, int8_t> ||
612 std::is_same_v<T, signed char>)
613 {
614 return MySqlTiny;
615 }
616 else if constexpr (std::is_same_v<T, uint8_t> ||
617 std::is_same_v<T, unsigned char>)
618 {
619 return MySqlUTiny;
620 }
621 else if constexpr (std::is_same_v<T, int16_t> ||
622 std::is_same_v<T, short>)
623 {
624 return MySqlShort;
625 }
626 else if constexpr (std::is_same_v<T, uint16_t> ||
627 std::is_same_v<T, unsigned short>)
628 {
629 return MySqlUShort;
630 }
631 else if constexpr (std::is_same_v<T, int32_t> ||
632 (std::is_same_v<T, int> && sizeof(int) == 4) ||
633 (std::is_same_v<T, long> && sizeof(long) == 4))
634 {
635 return MySqlLong;
636 }
637 else if constexpr (std::is_same_v<T, uint32_t> ||
638 (std::is_same_v<T, unsigned int> &&
639 sizeof(unsigned int) == 4) ||
640 (std::is_same_v<T, unsigned long> &&
641 sizeof(unsigned long) == 4))
642 {
643 return MySqlULong;
644 }
645 else if constexpr (std::is_same_v<T, int64_t> ||
646 std::is_same_v<T, long long> ||
647 (std::is_same_v<T, long> && sizeof(long) == 8))
648 {
649 return MySqlLongLong;
650 }
651 else if constexpr (std::is_same_v<T, uint64_t> ||
652 std::is_same_v<T, unsigned long long> ||
653 (std::is_same_v<T, unsigned long> &&
654 sizeof(unsigned long) == 8))
655 {
656 return MySqlULongLong;
657 }
658 else if constexpr (std::is_same_v<T, char>)
659 {
660 if constexpr (std::is_signed_v<char>)
661 {
662 return MySqlTiny;
663 }
664 else
665 {
666 return MySqlUTiny;
667 }
668 }
669 else
670 {
671 static_assert(sizeof(T) == 0, "Unsupported type for MySQL binding");
672 }
673 }
674
675 std::shared_ptr<std::string> sqlPtr_;
676 const char *sqlViewPtr_;
677 size_t sqlViewLength_;
678 DbClient &client_;
679 size_t parametersNumber_{0};
680 std::vector<const char *> parameters_;
681 std::vector<int> lengths_;
682 std::vector<int> formats_;
683 std::vector<std::shared_ptr<void>> objs_;
684 Mode mode_{Mode::NonBlocking};
685 std::shared_ptr<CallbackHolderBase> callbackHolder_;
686 DrogonDbExceptionCallback exceptionCallback_;
687 ExceptPtrCallback exceptionPtrCallback_;
688 bool execed_{false};
689 bool destructed_{false};
690 bool isExceptionPtr_{false};
691 ClientType type_;
692};
693
694} // namespace internal
695} // namespace orm
696} // namespace drogon
std::function< void(const DrogonDbException &)> DrogonDbExceptionCallback
Definition Exception.h:711
Database client abstract class.
Definition DbClient.h:105
Definition DbTypes.h:22
Reference to a field in a result set.
Definition Field.h:44
T as() const
Convert to a type T value.
Definition Field.h:71
Result set containing data returned by a query or command.
Definition Result.h:58
Reference to one row in a result.
Definition Row.h:47
Definition SqlBinder.h:195
Drogon Test is a minimal effort test framework developed because the major C++ test frameworks doesn'...
Definition Attribute.h:23
STL namespace.
Definition SqlBinder.h:121
Definition FunctionTraits.h:50
Definition SqlBinder.h:132