/** * * @file SqlBinder.h * @author An Tao * * Copyright 2018, An Tao. All rights reserved. * https://github.com/an-tao/drogon * Use of this source code is governed by a MIT license * that can be found in the License file. * * Drogon * */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #else // some Unix-like OS #include #endif #if defined __linux__ || defined __FreeBSD__ || defined __OpenBSD__ || \ defined __MINGW32__ || defined __HAIKU__ #ifdef __linux__ #include // __BYTE_ORDER __LITTLE_ENDIAN #elif defined __FreeBSD__ || defined __OpenBSD__ #include // _BYTE_ORDER _LITTLE_ENDIAN #define __BYTE_ORDER _BYTE_ORDER #define __LITTLE_ENDIAN _LITTLE_ENDIAN #elif defined __MINGW32__ #include // BYTE_ORDER LITTLE_ENDIAN #define __BYTE_ORDER BYTE_ORDER #define __LITTLE_ENDIAN LITTLE_ENDIAN #endif #include // std::reverse() template constexpr T htonT(T value) noexcept { #if __BYTE_ORDER == __LITTLE_ENDIAN char *ptr = reinterpret_cast(&value); std::reverse(ptr, ptr + sizeof(T)); #endif return value; } inline uint64_t htonll(uint64_t value) { return htonT(value); } inline uint64_t ntohll(uint64_t value) { return htonll(value); } #endif namespace drogon { namespace orm { enum class ClientType { PostgreSQL = 0, Mysql, Sqlite3 }; enum Sqlite3Type { Sqlite3TypeChar = 0, Sqlite3TypeShort, Sqlite3TypeInt, Sqlite3TypeInt64, Sqlite3TypeDouble, Sqlite3TypeText, Sqlite3TypeBlob, Sqlite3TypeNull }; class DbClient; using QueryCallback = std::function; using ExceptPtrCallback = std::function; enum class Mode { NonBlocking, Blocking }; namespace internal { template struct VectorTypeTraits { static const bool isVector = false; static const bool isPtrVector = false; using ItemsType = T; }; template struct VectorTypeTraits>> { static const bool isVector = true; static const bool isPtrVector = true; using ItemsType = T; }; template <> struct VectorTypeTraits { static const bool isVector = false; static const bool isPtrVector = false; using ItemsType = std::string; }; // we only accept value type or const lreference type or rreference type as // handle method parameters type template struct CallbackArgTypeTraits { static const bool isValid = true; }; template struct CallbackArgTypeTraits { static const bool isValid = false; }; template struct CallbackArgTypeTraits { static const bool isValid = false; }; template struct CallbackArgTypeTraits { static const bool isValid = true; }; template struct CallbackArgTypeTraits { static const bool isValid = true; }; class CallbackHolderBase { public: virtual ~CallbackHolderBase() = default; virtual void execCallback(const Result &result) = 0; }; template class CallbackHolder : public CallbackHolderBase { public: void execCallback(const Result &result) override { run(result); } template explicit CallbackHolder(T &&function) : function_(std::forward(function)) { static_assert(traits::isSqlCallback, "Your sql callback function type is wrong!"); } private: Function function_; using traits = FunctionTraits; template using NthArgumentType = typename traits::template argument; static const size_t argumentCount = traits::arity; template typename std::enable_if::type run(const Result &result) { if (result.empty()) { run(nullptr, true); return; } for (auto const &row : result) { run(&row, false); } run(nullptr, true); } template typename std::enable_if::type run(const Result &result) { static_assert(argumentCount == 0, "Your sql callback function type is wrong!"); function_(result); } template typename std::enable_if<(sizeof...(Values) < Boundary), void>::type run( const Row *const row, bool isNull, Values &&...values) { // call this function recursively until parameter's count equals to the // count of target function parameters static_assert( CallbackArgTypeTraits>::isValid, "your sql callback function argument type must be value " "type or " "const " "left-reference type"); using ValueType = typename std::remove_cv>::type>::type; ValueType value = ValueType(); if (row && row->size() > sizeof...(Values)) { // if(!VectorTypeTraits::isVector) // value = (*row)[sizeof...(Values)].as(); // else // ; // value = // (*row)[sizeof...(Values)].asArray::ItemsType>(); value = makeValue((*row)[(Row::SizeType)sizeof...(Values)]); } run(row, isNull, std::forward(values)..., std::move(value)); } template typename std::enable_if<(sizeof...(Values) == Boundary), void>::type run( const Row *const, bool isNull, Values &&...values) { function_(isNull, std::move(values)...); } template typename std::enable_if::isVector, ValueType>::type makeValue(const Field &field) { return field.asArray::ItemsType>(); } template typename std::enable_if::isVector, ValueType>::type makeValue(const Field &field) { return field.as(); } }; class DROGON_EXPORT SqlBinder : public trantor::NonCopyable { using self = SqlBinder; public: SqlBinder(const std::string &sql, DbClient &client, ClientType type) : sqlPtr_(std::make_shared(sql)), sqlViewPtr_(sqlPtr_->data()), sqlViewLength_(sqlPtr_->length()), client_(client), type_(type) { } SqlBinder(std::string &&sql, DbClient &client, ClientType type) : sqlPtr_(std::make_shared(std::move(sql))), sqlViewPtr_(sqlPtr_->data()), sqlViewLength_(sqlPtr_->length()), client_(client), type_(type) { } SqlBinder(const char *sql, size_t sqlLength, DbClient &client, ClientType type) : sqlViewPtr_(sql), sqlViewLength_(sqlLength), client_(client), type_(type) { } SqlBinder(SqlBinder &&that) noexcept : sqlPtr_(std::move(that.sqlPtr_)), sqlViewPtr_(that.sqlViewPtr_), sqlViewLength_(that.sqlViewLength_), client_(that.client_), parametersNumber_(that.parametersNumber_), parameters_(std::move(that.parameters_)), lengths_(std::move(that.lengths_)), formats_(std::move(that.formats_)), objs_(std::move(that.objs_)), mode_(that.mode_), callbackHolder_(std::move(that.callbackHolder_)), exceptionCallback_(std::move(that.exceptionCallback_)), exceptionPtrCallback_(std::move(that.exceptionPtrCallback_)), execed_(that.execed_), destructed_(that.destructed_), isExceptionPtr_(that.isExceptionPtr_), type_(that.type_) { // set the execed_ to true to avoid the same sql being executed twice. that.execed_ = true; } SqlBinder &operator=(SqlBinder &&that) = delete; ~SqlBinder(); template ::type>> typename std::enable_if::type & operator>>(CallbackType &&callback) { // LOG_DEBUG << "ptr callback"; isExceptionPtr_ = true; exceptionPtrCallback_ = std::forward(callback); return *this; } template ::type>> typename std::enable_if::type & operator>>(CallbackType &&callback) { isExceptionPtr_ = false; exceptionCallback_ = std::forward(callback); return *this; } template ::type>> typename std::enable_if::type &operator>>( CallbackType &&callback) { callbackHolder_ = std::shared_ptr( new CallbackHolder::type>( std::forward(callback))); return *this; } template typename std::enable_if< !std::is_same::type>::type, trantor::Date>::value, self &>::type operator<<(T &¶meter) { ++parametersNumber_; using ParaType = typename std::remove_cv< typename std::remove_reference::type>::type; std::shared_ptr obj = std::make_shared(parameter); if (type_ == ClientType::PostgreSQL) { switch (sizeof(T)) { case 2: *std::static_pointer_cast(obj) = htons((uint16_t)parameter); break; case 4: *std::static_pointer_cast(obj) = htonl((uint32_t)parameter); break; case 8: *std::static_pointer_cast(obj) = htonll((uint64_t)parameter); break; case 1: default: break; } objs_.push_back(obj); parameters_.push_back((char *)obj.get()); lengths_.push_back(sizeof(T)); formats_.push_back(1); } else if (type_ == ClientType::Mysql) { objs_.push_back(obj); parameters_.push_back((char *)obj.get()); lengths_.push_back(0); formats_.push_back(getMysqlTypeBySize(sizeof(T))); } else if (type_ == ClientType::Sqlite3) { objs_.push_back(obj); parameters_.push_back((char *)obj.get()); lengths_.push_back(0); switch (sizeof(T)) { case 1: formats_.push_back(Sqlite3TypeChar); break; case 2: formats_.push_back(Sqlite3TypeShort); break; case 4: formats_.push_back(Sqlite3TypeInt); break; case 8: formats_.push_back(Sqlite3TypeInt64); default: break; } } // LOG_TRACE << "Bind parameter:" << parameter; return *this; } // template <> self &operator<<(const char str[]) { return operator<<(std::string(str)); } self &operator<<(char str[]) { return operator<<(std::string(str)); } self &operator<<(const drogon::string_view &str); self &operator<<(drogon::string_view &&str) { return operator<<((const drogon::string_view &)str); } self &operator<<(drogon::string_view &str) { return operator<<((const drogon::string_view &)str); } self &operator<<(const std::string &str); self &operator<<(std::string &str) { return operator<<((const std::string &)str); } self &operator<<(std::string &&str); self &operator<<(trantor::Date &&date) { return operator<<(date.toDbStringLocal()); } self &operator<<(const trantor::Date &date) { return operator<<(date.toDbStringLocal()); } self &operator<<(const std::vector &v); self &operator<<(std::vector &v) { return operator<<((const std::vector &)v); } self &operator<<(std::vector &&v); self &operator<<(float f) { if (type_ == ClientType::Sqlite3) { return operator<<((double)f); } return operator<<(std::to_string(f)); } self &operator<<(double f); self &operator<<(std::nullptr_t); self &operator<<(DefaultValue dv); self &operator<<(const Mode &mode) { mode_ = mode; return *this; } self &operator<<(Mode &mode) { mode_ = mode; return *this; } self &operator<<(Mode &&mode) { mode_ = mode; return *this; } template self &operator<<(const optional ¶meter) { if (parameter) { return *this << parameter.value(); } return *this << nullptr; } template self &operator<<(optional ¶meter) { if (parameter) { return *this << parameter.value(); } return *this << nullptr; } template self &operator<<(optional &¶meter) { if (parameter) { return *this << std::move(parameter.value()); } return *this << nullptr; } self &operator<<(const Json::Value &j) noexcept(true) { switch (j.type()) { case Json::nullValue: return *this << nullptr; case Json::intValue: return *this << j.asInt64(); case Json::uintValue: return *this << j.asUInt64(); case Json::realValue: return *this << j.asDouble(); case Json::stringValue: return *this << j.asString(); case Json::booleanValue: return *this << j.asBool(); case Json::arrayValue: case Json::objectValue: default: static Json::StreamWriterBuilder jsonBuilder; std::once_flag once_json; std::call_once(once_json, []() { jsonBuilder["indentation"] = ""; }); return *this << Json::writeString(jsonBuilder, j); } } self &operator<<(Json::Value &j) noexcept(true) { return *this << static_cast(j); } self &operator<<(Json::Value &&j) noexcept(true) { return *this << static_cast(j); } void exec() noexcept(false); private: static int getMysqlTypeBySize(size_t size); std::shared_ptr sqlPtr_; const char *sqlViewPtr_; size_t sqlViewLength_; DbClient &client_; size_t parametersNumber_{0}; std::vector parameters_; std::vector lengths_; std::vector formats_; std::vector> objs_; Mode mode_{Mode::NonBlocking}; std::shared_ptr callbackHolder_; DrogonDbExceptionCallback exceptionCallback_; ExceptPtrCallback exceptionPtrCallback_; bool execed_{false}; bool destructed_{false}; bool isExceptionPtr_{false}; ClientType type_; }; } // namespace internal } // namespace orm } // namespace drogon