diff --git a/orm_lib/inc/drogon/orm/Criteria.h b/orm_lib/inc/drogon/orm/Criteria.h new file mode 100644 index 00000000..ffb9a75d --- /dev/null +++ b/orm_lib/inc/drogon/orm/Criteria.h @@ -0,0 +1,164 @@ +/** + * + * Criteria.h + * An Tao + * + * Copyright 2018, An Tao. All rights reserved. + * Use of this source code is governed by a MIT license + * that can be found in the License file. + * + * + */ + +#include +#include + +#include +#include +#include +#include +#include + +namespace drogon +{ +namespace orm +{ + +enum class CompareOperator +{ + EQ, + NE, + GT, + GE, + LT, + LE, + IsNull, + IsNotNull +}; +class Criteria +{ + public: + explicit operator bool() const { return !_condString.empty(); } + std::string criteriaString() { return _condString; } + template + Criteria(const std::string &colName, const CompareOperator &opera, T &&arg) + { + static_assert(opera != CompareOperator::IsNotNull && + opera != CompareOperator::IsNull, + "Invalid compare operator!"); + _condString = colName; + switch (opera) + { + case CompareOperator::EQ: + _condString += " = $?"; + break; + case CompareOperator::NE: + _condString += " != $?"; + break; + case CompareOperator::GT: + _condString += " > $?"; + break; + case CompareOperator::GE: + _condString += " >= $?"; + break; + case CompareOperator::LT: + _condString += " < $?"; + break; + case CompareOperator::LE: + _condString += " <= $?"; + break; + case CompareOperator::IsNull: + case CompareOperator::IsNotNull: + default: + break; + } + _outputArgumentsFunc = [=](inner::SqlBinder &binder) { + binder << arg; + }; + } + template + Criteria(const std::string &colName, T &&arg) + : Criteria(colName, CompareOperator::EQ, arg) + { + } + + Criteria(const std::string &colName, const CompareOperator &opera) + { + assert(opera == CompareOperator::IsNotNull || + opera == CompareOperator::IsNull); + _condString = colName; + switch (opera) + { + case CompareOperator::IsNull: + _condString += " is null"; + break; + case CompareOperator::IsNotNull: + _condString += " is not null"; + break; + default: + break; + } + } + Criteria() {} + void outputArgs(internal::SqlBinder &binder) + { + if (_outputArgumentsFunc) + _outputArgumentsFunc(binder); + } + + private: + friend const Criteria operator&&(Criteria cond1, Criteria cond2); + friend const Criteria operator||(Criteria cond1, Criteria cond2); + std::string _condString; + std::function _outputArgumentsFunc; +}; // namespace orm + +const Criteria operator&&(Criteria cond1, Criteria cond2) +{ + assert(cond1 && cond2); + Criteria cond; + cond._condString = "( "; + cond._condString += cond1._condString; + cond._condString += " ) and ( "; + cond._condString += cond2._condString; + cond._condString += " )"; + auto cond1Ptr = std::make_shared(std::move(cond1)); + auto cond2Ptr = std::make_shared(std::move(cond2)); + cond._outputArgumentsFunc = [=](internal::SqlBinder &binder) { + if (cond1Ptr->_outputArgumentsFunc) + { + cond1Ptr->_outputArgumentsFunc(binder); + } + if (cond2Ptr->_outputArgumentsFunc) + { + cond2Ptr->_outputArgumentsFunc(binder); + } + }; + return cond; +} +const Criteria operator||(Criteria cond1, Criteria cond2) +{ + assert(cond1 && cond2); + Criteria cond; + cond._condString = "( "; + cond._condString += cond1._condString; + cond._condString += " ) or ( "; + cond._condString += cond2._condString; + cond._condString += " )"; + auto cond1Ptr = std::make_shared(std::move(cond1)); + auto cond2Ptr = std::make_shared(std::move(cond2)); + cond._outputArgumentsFunc = [=](internal::SqlBinder &binder) { + if (cond1Ptr->_outputArgumentsFunc) + { + cond1Ptr->_outputArgumentsFunc(binder); + } + if (cond2Ptr->_outputArgumentsFunc) + { + cond2Ptr->_outputArgumentsFunc(binder); + } + }; + return cond; +} + +} // namespace orm +} // namespace drogon \ No newline at end of file diff --git a/orm_lib/inc/drogon/orm/DbClient.h b/orm_lib/inc/drogon/orm/DbClient.h index f31646e4..8cff4957 100644 --- a/orm_lib/inc/drogon/orm/DbClient.h +++ b/orm_lib/inc/drogon/orm/DbClient.h @@ -96,8 +96,9 @@ public: } internal::SqlBinder operator<<(const std::string &sql); + virtual std::string replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const = 0; -private: + private: friend internal::SqlBinder; virtual void execSql(const std::string &sql, size_t paraNum, diff --git a/orm_lib/inc/drogon/orm/Mapper.h b/orm_lib/inc/drogon/orm/Mapper.h index 00dd62b5..8cb815ef 100644 --- a/orm_lib/inc/drogon/orm/Mapper.h +++ b/orm_lib/inc/drogon/orm/Mapper.h @@ -13,18 +13,31 @@ #pragma once #include +#include namespace drogon { namespace orm { -class Condition; + +enum class SortOrder +{ + ASC, + DESC +}; template class Mapper { public: + Mapper &limit(size_t limit); + Mapper &offset(size_t offset); + Mapper &orderBy(const std::string &colName, const SortOrder &order = SortOrder::ASC); + Mapper &orderBy(size_t colIndex, const SortOrder &order = SortOrder::ASC); + typedef std::function SingleRowCallback; typedef std::function)> MultipleRowsCallback; + typedef std::function CountCallback; + Mapper(const DbClient &client) : _client(client) {} T findByPrimaryKey(const T::PrimaryKeyType &key) noexcept(false); @@ -38,18 +51,177 @@ class Mapper const ExceptionCallback &ecb) noexcept; std::future> findFutureAll() noexcept; - //find one - //find by conditions - //insert - //update - //update all - //count - //limit/offset/orderby - //... + size_t count(const Criteria &criteria) noexcept(false); + void count(const Criteria &criteria, + const CountCallback &rcb, + const ExceptionCallback &ecb) noexcept; + std::future count(const Criteria &criteria) noexcept; + + T findOne(const Criteria &criteria) noexcept(false); + void findOne(const Criteria &criteria, + const SingleRowCallback &rcb, + const ExceptionCallback &ecb) noexcept; + std::future findFutureOne(const Criteria &criteria) noexcept; + + std::vector findBy(const Criteria &criteria) noexcept(false); + void findBy(const Criteria &criteria, + const MultipleRowsCallback &rcb, + const ExceptionCallback &ecb) noexcept; + std::future> findFutureBy(const Criteria &criteria) noexcept; + + void insert(T &obj) noexcept(false); + void insert(const T &obj, + const SingleRowCallback &rcb, + const ExceptionCallback &ecb) noexcept; + std::future insert(const T &) noexcept; + + size_t update(T &obj) noexcept(false); + void update(const T &obj, + const CountCallback &rcb, + const ExceptionCallback &ecb) noexcept; + std::future update(const T &obj) noexcept; + private: Dbclient &_client; std::string getSqlForFindingByPrimaryKey(); }; +template +inline T Mapper::findByPrimaryKey(const T::PrimaryKeyType &key) noexcept(false) +{ + static_assert(T::hasPrimaryKey, "No primary key in the table!"); + return findOne(Criteria(T::primaryKeyName, key)); +} +template +inline void Mapper::findByPrimaryKey(const T::PrimaryKeyType &key, + const SingleRowCallback &rcb, + const ExceptionCallback &ecb) noexcept +{ + static_assert(T::hasPrimaryKey, "No primary key in the table!"); + findOne(Criteria(T::primaryKeyName, key), rcb, ecb); +} +template +inline std::future Mapper::findFutureByPrimaryKey(const T::PrimaryKeyType &key) noexcept +{ + static_assert(T::hasPrimaryKey, "No primary key in the table!"); + return findFutureOne(Criteria(T::primaryKeyName, key)); +} +template +inline T Mapper::findOne(const Criteria &criteria) noexcept(false) +{ + std::string sql = "select * from "; + sql += T::tableName; + if (criteria) + { + sql += " where "; + sql += criteria.criteriaString(); + sql = _client.replaceSqlPlaceHolder(sql, "$?"); + } + Result r(nullptr); + { + auto binder = _client << sql; + if (criteria) + criteria.outputArgs(binder); + binder << Mode::Blocking; + binder >> [&r](const Result &result) { + r = result; + }; + binder.exec(); //exec may be throw exception; + } + if (r.size() == 0) + { + throw Failure("No records found that meet the criteria"); + } + else if (r.size() > 1) + { + throw Failure("More than one record found"); + } + auto row = r[0]; + return T(row); +} +template +inline void Mapper::findOne(const Criteria &criteria, + const SingleRowCallback &rcb, + const ExceptionCallback &ecb) noexcept +{ + std::string sql = "select * from "; + sql += T::tableName; + if (criteria) + { + sql += " where "; + sql += criteria.criteriaString(); + sql = _client.replaceSqlPlaceHolder(sql, "$?"); + } + + auto binder = _client << sql; + if (criteria) + criteria.outputArgs(binder); + binder >> [=](const Result &r) { + if (r.size() == 0) + { + ecb(Failure("No records found that meet the criteria")); + } + else if (r.size() > 1) + { + ech(Failure("More than one record found")); + } + else + { + rcb(T(r[0])); + } + }; + binder >> ecb; +} +template +inline std::future Mapper::findFutureOne(const Criteria &criteria) noexcept +{ + std::string sql = "select * from "; + sql += T::tableName; + if (criteria) + { + sql += " where "; + sql += criteria.criteriaString(); + sql = _client.replaceSqlPlaceHolder(sql, "$?"); + } + + auto binder = _client << sql; + if (criteria) + criteria.outputArgs(binder); + + std::shared_ptr> prom = std::make_shared>(); + binder >> [=](const Result &r) { + if (r.size() == 0) + { + try + { + throw Failure("No records found that meet the criteria"); + } + catch (...) + { + prom->set_exception(std::current_exception()); + } + } + else if (r.size() > 1) + { + try + { + throw Failure("More than one record found"); + } + catch (...) + { + prom->set_exception(std::current_exception()); + } + } + else + { + prom->set_value(T(r[0])); + } + }; + binder >> [=](const std::exception_ptr &e) { + prom->set_exception(e); + }; + binder.exec(); + return prom->get_future(); +} } // namespace orm } // namespace drogon diff --git a/orm_lib/inc/drogon/orm/PgClient.h b/orm_lib/inc/drogon/orm/PgClient.h index 39b7b4cc..8ec805d6 100644 --- a/orm_lib/inc/drogon/orm/PgClient.h +++ b/orm_lib/inc/drogon/orm/PgClient.h @@ -23,6 +23,7 @@ class PgClient : public DbClient { public: PgClient(const std::string &connInfo, const size_t connNum); + virtual std::string replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const override; private: virtual void execSql(const std::string &sql, diff --git a/orm_lib/src/postgresql_impl/PgClient.cc b/orm_lib/src/postgresql_impl/PgClient.cc index c0674acb..2b2c5743 100644 --- a/orm_lib/src/postgresql_impl/PgClient.cc +++ b/orm_lib/src/postgresql_impl/PgClient.cc @@ -28,22 +28,12 @@ void PgClient::execSql(const std::string &sql, const ResultCallback &rcb, const std::function &exceptCallback) { - // std::thread _thread([=]() { - // try - // { - // throw Failure("exception test!!!"); - // } - // catch (...) - // { - // exceptCallback(std::current_exception()); - // } - // }); - // _thread.detach(); - // rcb(Result()); _clientPtr->execSql(sql,paraNum,parameters,length,format,rcb,exceptCallback); } -PgClient::PgClient(const std::string &connInfo, const size_t connNum): -_clientPtr(new PgClientImpl(connInfo,connNum)) +std::string PgClient::replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const { + return _clientPtr->replaceSqlPlaceHolder(sqlStr, holderStr); +} +PgClient::PgClient(const std::string &connInfo, const size_t connNum) : _clientPtr(new PgClientImpl(connInfo, connNum)) { } \ No newline at end of file diff --git a/orm_lib/src/postgresql_impl/PgClientImpl.cc b/orm_lib/src/postgresql_impl/PgClientImpl.cc index 69c96684..31154404 100644 --- a/orm_lib/src/postgresql_impl/PgClientImpl.cc +++ b/orm_lib/src/postgresql_impl/PgClientImpl.cc @@ -16,6 +16,8 @@ #include #include #include +#include + using namespace drogon::orm; DbConnectionPtr PgClientImpl::newConnection(trantor::EventLoop *loop) @@ -34,7 +36,7 @@ DbConnectionPtr PgClientImpl::newConnection(trantor::EventLoop *loop) //std::cout<<"Conn closed!end"<setOkCallback([=](const DbConnectionPtr &okConnPtr) { - LOG_TRACE << "postgreSQL connected!" ; + LOG_TRACE << "postgreSQL connected!"; std::lock_guard guard(_connectionsMutex); _readyConnections.insert(okConnPtr); }); @@ -151,7 +153,7 @@ void PgClientImpl::execSql(const std::string &sql, { throw BrokenConnection("No connection to postgreSQL server"); } - catch(...) + catch (...) { exceptCb(std::current_exception()); } @@ -200,3 +202,24 @@ void PgClientImpl::execSql(const std::string &sql, _sqlCmdBuffer.push_back(std::move(cmd)); } } + +std::string PgClientImpl::replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const +{ + std::string::size_type startPos = 0; + std::string::size_type pos; + std::stringstream ret; + size_t phCount = 1; + do + { + pos = sqlStr.find(holderStr, startPos); + if (pos == std::string::npos) + { + ret << sqlStr.substr(startPos); + return ret.str(); + } + ret << sqlStr.substr(startPos, pos - startPos); + ret << "$"; + ret << phCount++; + startPos = pos + holderStr.length(); + } while (1); +} diff --git a/orm_lib/src/postgresql_impl/PgClientImpl.h b/orm_lib/src/postgresql_impl/PgClientImpl.h index 9cee8063..e1c0c778 100644 --- a/orm_lib/src/postgresql_impl/PgClientImpl.h +++ b/orm_lib/src/postgresql_impl/PgClientImpl.h @@ -29,6 +29,7 @@ class PgClientImpl : public trantor::NonCopyable const std::vector &format, const ResultCallback &rcb, const std::function &exceptCallback); + std::string replaceSqlPlaceHolder(const std::string &sqlStr, const std::string &holderStr) const; private: void ioLoop();