Start Adding orm

This commit is contained in:
antao 2018-10-28 16:00:37 +08:00 committed by an-tao
parent 373a423dfe
commit 2dcb924c81
31 changed files with 3267 additions and 1 deletions

View File

@ -19,7 +19,7 @@ else()
set(CMAKE_CXX_STD_FLAGS c++17)
endif()
include_directories(${PROJECT_SOURCE_DIR}/trantor ${PROJECT_SOURCE_DIR}/lib/inc)
include_directories(${PROJECT_SOURCE_DIR}/trantor ${PROJECT_SOURCE_DIR}/lib/inc ${PROJECT_SOURCE_DIR}/orm_lib/inc)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake_modules/)
#jsoncpp
@ -61,6 +61,15 @@ link_libraries(${ZLIB_LIBRARIES})
message(STATUS "zlib inc path:" ${ZLIB_INCLUDE_DIR})
#find postgres
find_package(PostgreSQL)
if(PostgreSQL_FOUND)
include_directories(${PostgreSQL_INCLUDE_DIR})
message(STATUS "libpq inc path:" ${PostgreSQL_INCLUDE_DIR})
link_libraries(${PostgreSQL_LIBRARIES})
aux_source_directory(${PROJECT_SOURCE_DIR}/orm_lib/src/postgresql_impl DIR_SRCS)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE Release)
endif()
@ -80,6 +89,7 @@ add_subdirectory(examples)
add_subdirectory(drogon_ctl)
aux_source_directory(${PROJECT_SOURCE_DIR}/lib/src DIR_SRCS)
aux_source_directory(${PROJECT_SOURCE_DIR}/orm_lib/src DIR_SRCS)
ADD_LIBRARY(drogon ${DIR_SRCS})
@ -113,6 +123,9 @@ EXEC_PROGRAM(${PROJECT_SOURCE_DIR}/update_config.sh ARGS "${CONFIG_HEADER} ${PRO
if (MAKETEST STREQUAL YES)
ADD_SUBDIRECTORY(tests)
if(PostgreSQL_FOUND)
add_subdirectory(${PROJECT_SOURCE_DIR}/orm_lib/src/postgresql_impl/test)
endif()
endif ()
SET(CMAKE_INSTALL_PREFIX /usr/local)

54
COPYING Executable file
View File

@ -0,0 +1,54 @@
MIT License
Copyright (c) 2018 drogon,an-tao
https://github.com/an-tao/drogon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The license for libpqxx is as follows. Source code for several classes, including the
Result class, the Row class, the Field class and their iterator classes are taken from
libpqxx and modified.
Copyright (c) 2001-2017 Jeroen T. Vermeulen.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author, nor the names of other contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,112 @@
/**
*
* DbClient.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.
*
*
*/
#pragma once
#include <drogon/orm/Exception.h>
#include <trantor/utils/Logger.h>
#include <trantor/utils/NonCopyable.h>
#include <drogon/orm/SqlBinder.h>
#include <drogon/orm/Result.h>
#include <drogon/orm/Row.h>
#include <drogon/orm/Field.h>
#include <drogon/orm/ResultIterator.h>
#include <drogon/orm/RowIterator.h>
#include <string>
#include <functional>
#include <exception>
#include <future>
namespace drogon
{
namespace orm
{
typedef std::function<void(const Result &)> ResultCallback;
typedef std::function<void(const std::exception_ptr &)> ExceptionCallback;
class DbClient : public trantor::NonCopyable
{
public:
virtual ~DbClient() {};
//Async method, nonblocking by default;
template <
typename FUNCTION1,
typename FUNCTION2,
typename... Arguments>
void execSqlAsync(const std::string &sql,
Arguments &&... args,
FUNCTION1 rCallback,
FUNCTION2 exceptCallback,
bool blocking = false) noexcept
{
auto binder = *this << sql;
std::vector<int> v = {(binder << std::forward<Arguments>(args), 0)...};
if (blocking)
{
binder << Mode::Blocking;
}
binder >> rCallback;
binder >> exceptCallback;
}
//Async and nonblocking method
template <typename... Arguments>
std::future<const Result> execSqlAsync(const std::string &sql,
Arguments &&... args) noexcept
{
auto binder = *this << sql;
std::vector<int> v = {(binder << std::forward<Arguments>(args), 0)...};
std::shared_ptr<std::promise<const Result>> prom=std::make_shared<std::promise<const Result>>();
binder >> [=](const Result &r) {
prom->set_value(r);
};
binder >> [=](const std::exception_ptr &e) {
prom->set_exception(e);
};
binder.exec();
return prom->get_future();
}
//Sync and blocking method
template <typename... Arguments>
const Result execSqlSync(const std::string &sql,
Arguments &&... args) noexcept(false)
{
Result r(nullptr);
{
auto binder = *this << sql;
std::vector<int> v = {(binder << std::forward<Arguments>(args), 0)...};
//Use blocking mode
binder << Mode::Blocking;
binder >> [&r](const Result &result) {
r = result;
};
binder.exec(); //exec may be throw exception;
}
return r;
}
internal::SqlBinder operator<<(const std::string &sql);
private:
friend internal::SqlBinder;
virtual void execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exptCallback) = 0;
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,489 @@
/** Definition of Drogon Db exception classes.
*
* Copyright (c) 2003-2018, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this mistake,
* or contact the author.
*/
//taken from libpqxx and modified
/**
*
* Exception.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.
*
*
*/
#pragma once
#include <stdexcept>
#include <string>
#include <functional>
namespace drogon
{
namespace orm
{
/// Mixin base class to identify drogon-db-specific exception types
/**
* If you wish to catch all exception types specific to drogon db for some reason,
* catch this type. All of drogon db's exception classes are derived from it
* through multiple-inheritance (they also fit into the standard library's
* exception hierarchy in more fitting places).
*
* This class is not derived from std::exception, since that could easily lead
* to exception classes with multiple std::exception base-class objects. As
* Bart Samwel points out, "catch" is subject to some nasty fineprint in such
* cases.
*/
class DrogonDbException
{
public:
/// Support run-time polymorphism, and keep this class abstract
virtual ~DrogonDbException() noexcept;
/// Return std::exception base-class object
/** Use this to get at the exception's what() function, or to downcast to a
* more specific type using dynamic_cast.
*
* Casting directly from DrogonDbException to a specific exception type is not
* likely to work since DrogonDbException is not (and could not safely be)
* derived from std::exception.
*
* For example, to test dynamically whether an exception is an SqlError:
*
* @code
* try
* {
* // ...
* }
* catch (const drogon::orm::DrogonDbException &e)
* {
* std::cerr << e.base().what() << std::endl;
* const drogon::orm::SqlError *s=dynamic_cast<const drogon::orm::SqlError*>(&e.base());
* if (s) std::cerr << "Query was: " << s->query() << std::endl;
* }
* @endcode
*/
virtual const std::exception &base() const noexcept {
static std::exception except;
return except;
} //[t00]
};
/// Run-time Failure encountered by drogon orm lib, similar to std::runtime_error
class Failure : public DrogonDbException, public std::runtime_error
{
virtual const std::exception &base() const noexcept override
{
return *this;
}
public:
explicit Failure(const std::string &);
};
/// Exception class for lost or failed backend connection.
/**
* @warning When this happens on Unix-like systems, you may also get a SIGPIPE
* signal. That signal aborts the program by default, so if you wish to be able
* to continue after a connection breaks, be sure to disarm this signal.
*
* If you're working on a Unix-like system, see the manual page for
* @c signal (2) on how to deal with SIGPIPE. The easiest way to make this
* signal harmless is to make your program ignore it:
*
* @code
* #include <signal.h>
*
* int main()
* {
* signal(SIGPIPE, SIG_IGN);
* // ...
* @endcode
*/
class BrokenConnection : public Failure
{
public:
BrokenConnection();
explicit BrokenConnection(const std::string &);
};
/// Exception class for failed queries.
/** Carries, in addition to a regular error message, a copy of the failed query
* and (if available) the SQLSTATE value accompanying the error.
*/
class SqlError : public Failure
{
/// Query string. Empty if unknown.
const std::string _query;
/// SQLSTATE string describing the error type, if known; or empty string.
const std::string _sqlState;
public:
explicit SqlError(
const std::string &msg = "",
const std::string &Q = "",
const char sqlstate[] = nullptr);
virtual ~SqlError() noexcept;
/// The query whose execution triggered the exception
const std::string &query() const noexcept;
const std::string &sqlState() const noexcept;
};
/// "Help, I don't know whether transaction was committed successfully!"
/** Exception that might be thrown in rare cases where the connection to the
* database is lost while finishing a database transaction, and there's no way
* of telling whether it was actually executed by the backend. In this case
* the database is left in an indeterminate (but consistent) state, and only
* manual inspection will tell which is the case.
*/
class InDoubtError : public Failure
{
public:
explicit InDoubtError(const std::string &);
};
/// The backend saw itself forced to roll back the ongoing transaction.
class TransactionRollback : public Failure
{
public:
explicit TransactionRollback(const std::string &);
};
/// Transaction failed to serialize. Please retry it.
/** Can only happen at transaction isolation levels REPEATABLE READ and
* SERIALIZABLE.
*
* The current transaction cannot be committed without violating the guarantees
* made by its isolation level. This is the effect of a conflict with another
* ongoing transaction. The transaction may still succeed if you try to
* perform it again.
*/
class SerializationFailure : public TransactionRollback
{
public:
explicit SerializationFailure(const std::string &);
};
/// We can't tell whether our last statement succeeded.
class StatementCompletionUnknown : public TransactionRollback
{
public:
explicit StatementCompletionUnknown(const std::string &);
};
/// The ongoing transaction has deadlocked. Retrying it may help.
class DeadlockDetected : public TransactionRollback
{
public:
explicit DeadlockDetected(const std::string &);
};
/// Internal error in internal library
class InternalError : public DrogonDbException, public std::logic_error
{
virtual const std::exception &base() const noexcept override
{
return *this;
}
public:
explicit InternalError(const std::string &);
};
/// Error in usage of drogon orm library, similar to std::logic_error
class UsageError : public DrogonDbException, public std::logic_error
{
virtual const std::exception &base() const noexcept override
{
return *this;
}
public:
explicit UsageError(const std::string &);
};
/// Invalid argument passed to drogon orm lib, similar to std::invalid_argument
class ArgumentError : public DrogonDbException, public std::invalid_argument
{
virtual const std::exception &base() const noexcept override
{
return *this;
}
public:
explicit ArgumentError(const std::string &);
};
/// Value conversion failed, e.g. when converting "Hello" to int.
class ConversionError : public DrogonDbException, public std::domain_error
{
virtual const std::exception &base() const noexcept override
{
return *this;
}
public:
explicit ConversionError(const std::string &);
};
/// Something is out of range, similar to std::out_of_range
class RangeError : public DrogonDbException, public std::out_of_range
{
virtual const std::exception &base() const noexcept override
{
return *this;
}
public:
explicit RangeError(const std::string &);
};
/// Query returned an unexpected number of rows.
class UnexpectedRows : public RangeError
{
virtual const std::exception &base() const noexcept override
{
return *this;
}
public:
explicit UnexpectedRows(const std::string &msg) : RangeError(msg) {}
};
/// Database feature not supported in current setup
class FeatureNotSupported : public SqlError
{
public:
explicit FeatureNotSupported(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SqlError(err, Q, sqlstate) {}
};
/// Error in data provided to SQL statement
class DataException : public SqlError
{
public:
explicit DataException(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SqlError(err, Q, sqlstate) {}
};
class IntegrityConstraintViolation : public SqlError
{
public:
explicit IntegrityConstraintViolation(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SqlError(err, Q, sqlstate) {}
};
class RestrictViolation : public IntegrityConstraintViolation
{
public:
explicit RestrictViolation(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : IntegrityConstraintViolation(err, Q, sqlstate) {}
};
class NotNullViolation : public IntegrityConstraintViolation
{
public:
explicit NotNullViolation(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : IntegrityConstraintViolation(err, Q, sqlstate) {}
};
class ForeignKeyViolation : public IntegrityConstraintViolation
{
public:
explicit ForeignKeyViolation(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : IntegrityConstraintViolation(err, Q, sqlstate) {}
};
class UniqueViolation : public IntegrityConstraintViolation
{
public:
explicit UniqueViolation(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : IntegrityConstraintViolation(err, Q, sqlstate) {}
};
class CheckViolation : public IntegrityConstraintViolation
{
public:
explicit CheckViolation(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : IntegrityConstraintViolation(err, Q, sqlstate) {}
};
class InvalidCursorState : public SqlError
{
public:
explicit InvalidCursorState(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SqlError(err, Q, sqlstate) {}
};
class InvalidSqlStatementName : public SqlError
{
public:
explicit InvalidSqlStatementName(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SqlError(err, Q, sqlstate) {}
};
class InvalidCursorName : public SqlError
{
public:
explicit InvalidCursorName(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SqlError(err, Q, sqlstate) {}
};
class SyntaxError : public SqlError
{
public:
/// Approximate position in string where error occurred, or -1 if unknown.
const int _errorPosition;
explicit SyntaxError(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr,
int pos = -1) : SqlError(err, Q, sqlstate), _errorPosition(pos) {}
};
class UndefinedColumn : public SyntaxError
{
public:
explicit UndefinedColumn(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SyntaxError(err, Q, sqlstate) {}
};
class UndefinedFunction : public SyntaxError
{
public:
explicit UndefinedFunction(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SyntaxError(err, Q, sqlstate) {}
};
class UndefinedTable : public SyntaxError
{
public:
explicit UndefinedTable(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SyntaxError(err, Q, sqlstate) {}
};
class InsufficientPrivilege : public SqlError
{
public:
explicit InsufficientPrivilege(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SqlError(err, Q, sqlstate) {}
};
/// Resource shortage on the server
class InsufficientResources : public SqlError
{
public:
explicit InsufficientResources(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : SqlError(err, Q, sqlstate) {}
};
class DiskFull : public InsufficientResources
{
public:
explicit DiskFull(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : InsufficientResources(err, Q, sqlstate) {}
};
class OutOfMemory : public InsufficientResources
{
public:
explicit OutOfMemory(
const std::string &err,
const std::string &Q = "",
const char sqlstate[] = nullptr) : InsufficientResources(err, Q, sqlstate) {}
};
class TooManyConnections : public BrokenConnection
{
public:
explicit TooManyConnections(const std::string &err) : BrokenConnection(err) {}
};
// /// PL/pgSQL error
// /** Exceptions derived from this class are errors from PL/pgSQL procedures.
// */
// class PQXX_LIBEXPORT plpgsql_error : public sql_error
// {
// public:
// explicit plpgsql_error(
// const std::string &err,
// const std::string &Q = "",
// const char sqlstate[] = nullptr) : sql_error(err, Q, sqlstate) {}
// };
// /// Exception raised in PL/pgSQL procedure
// class PQXX_LIBEXPORT plpgsql_raise : public plpgsql_error
// {
// public:
// explicit plpgsql_raise(
// const std::string &err,
// const std::string &Q = "",
// const char sqlstate[] = nullptr) : plpgsql_error(err, Q, sqlstate) {}
// };
// class PQXX_LIBEXPORT plpgsql_no_data_found : public plpgsql_error
// {
// public:
// explicit plpgsql_no_data_found(
// const std::string &err,
// const std::string &Q = "",
// const char sqlstate[] = nullptr) : plpgsql_error(err, Q, sqlstate) {}
// };
// class PQXX_LIBEXPORT plpgsql_too_many_rows : public plpgsql_error
// {
// public:
// explicit plpgsql_too_many_rows(
// const std::string &err,
// const std::string &Q = "",
// const char sqlstate[] = nullptr) : plpgsql_error(err, Q, sqlstate) {}
// };
typedef std::function<void(const DrogonDbException &)> DrogonDbExceptionCallback;
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,126 @@
/**
*
* Field.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.
*
* @section DESCRIPTION
*
*/
//Taken from libpqxx and modified.
//The license for libpqxx can be found in the COPYING file.
#pragma once
#include <drogon/orm/Result.h>
#include <drogon/orm/Row.h>
#ifdef __linux__
#include <arpa/inet.h>
inline uint64_t
ntohll(const uint64_t &input)
{
uint64_t rval;
uint8_t *data = (uint8_t *)&rval;
data[0] = input >> 56;
data[1] = input >> 48;
data[2] = input >> 40;
data[3] = input >> 32;
data[4] = input >> 24;
data[5] = input >> 16;
data[6] = input >> 8;
data[7] = input >> 0;
return rval;
}
inline uint64_t
htonll(const uint64_t &input)
{
return (ntohll(input));
}
#endif
#ifdef _WIN32
inline uint64_t
ntohll(const uint64_t &input)
{
uint64_t rval;
uint8_t *data = (uint8_t *)&rval;
data[0] = input >> 56;
data[1] = input >> 48;
data[2] = input >> 40;
data[3] = input >> 32;
data[4] = input >> 24;
data[5] = input >> 16;
data[6] = input >> 8;
data[7] = input >> 0;
return rval;
}
inline uint64_t
htonll(const uint64_t &input)
{
return (ntohll(input));
}
#endif
namespace drogon
{
namespace orm
{
class Field
{
public:
using size_type = unsigned long;
const char *name() const;
bool isNull() const;
template <typename T>
T as() const
{
auto _data = _result.getValue(_row, _column);
auto _dataLength = _result.getLength(_row, _column);
if (_dataLength == 1)
{
return *_data;
}
else if (_dataLength == 4)
{
const int32_t *n = (int32_t *)_data;
return ntohl(*n);
}
else if (_dataLength == 8)
{
const int64_t *n = (int64_t *)_data;
return ntohll(*n);
}
return 0;
}
protected:
Result::size_type _row;
/**
* Column number
* You'd expect this to be a size_t, but due to the way reverse iterators
* are related to regular iterators, it must be allowed to underflow to -1.
*/
long _column;
friend class Row;
Field(const Row &row, Row::size_type columnNum) noexcept;
private:
Result _result;
};
template <>
std::string Field::as<std::string>() const;
template <>
const char *Field::as<const char *>() const;
template <>
char *Field::as<char *>() const;
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,127 @@
/**
*
* FunctionTraits.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.
*
*
*/
#pragma once
#include <tuple>
#include <type_traits>
#include <string>
#include <exception>
namespace drogon
{
namespace orm
{
class Result;
class DrogonDbException;
namespace internal
{
template <typename>
struct FunctionTraits;
template <>
struct FunctionTraits<void (*)()>
{
typedef void result_type;
template <std::size_t Index>
using argument = void;
static const std::size_t arity = 0;
static const bool isSqlCallback = false;
static const bool isExceptCallback = false;
};
//functor,lambda
template <typename Function>
struct FunctionTraits : public FunctionTraits<
decltype(&std::remove_reference<Function>::type::operator())>
{
};
template <
typename ClassType,
typename ReturnType,
typename... Arguments>
struct FunctionTraits<
ReturnType (ClassType::*)(Arguments...) const> : FunctionTraits<ReturnType (*)(Arguments...)>
{
};
template <
typename ClassType,
typename ReturnType,
typename... Arguments>
struct FunctionTraits<
ReturnType (ClassType::*)(Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>
{
};
template <>
struct FunctionTraits<void (*)(const Result &)>
: public FunctionTraits<void (*)()>
{
static const bool isSqlCallback = true;
static const bool isStepResultCallback = false;
static const bool isExceptCallback = false;
};
template <>
struct FunctionTraits<void (*)(const DrogonDbException &)>
: public FunctionTraits<void (*)()>
{
static const bool isExceptCallback = true;
static const bool isSqlCallback = false;
static const bool isStepResultCallback = false;
static const bool isPtr = false;
};
template <>
struct FunctionTraits<void (*)(const std::exception_ptr &)>
: public FunctionTraits<void (*)()>
{
static const bool isExceptCallback = true;
static const bool isSqlCallback = false;
static const bool isStepResultCallback = false;
static const bool isPtr = true;
};
template <
typename ReturnType,
typename... Arguments>
struct FunctionTraits<
ReturnType (*)(bool,Arguments...)> : FunctionTraits<ReturnType (*)(Arguments...)>
{
static const bool isSqlCallback = true;
static const bool isStepResultCallback = true;
};
template <
typename ReturnType,
typename... Arguments>
struct FunctionTraits<
ReturnType (*)(Arguments...)>
{
typedef ReturnType result_type;
template <std::size_t Index>
using argument = typename std::tuple_element<
Index,
std::tuple<Arguments...>>::type;
static const std::size_t arity = sizeof...(Arguments);
//static const bool isSqlCallback = false;
static const bool isSqlCallback = true;
static const bool isStepResultCallback = true;
};
} // namespace internal
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,55 @@
/**
*
* Mapper.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.
*
* @section DESCRIPTION
*
*/
#pragma once
#include <drogon/orm/DbClient.h>
namespace drogon
{
namespace orm
{
class Condition;
template <typename T>
class Mapper
{
public:
typedef std::function<void(T)> SingleRowCallback;
typedef std::function<void(std::vector<T>)> MultipleRowsCallback;
Mapper(const DbClient &client) : _client(client) {}
T findByPrimaryKey(const T::PrimaryKeyType &key) noexcept(false);
void findByPrimaryKey(const T::PrimaryKeyType &key,
const SingleRowCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<T> findFutureByPrimaryKey(const T::PrimaryKeyType &key) noexcept;
std::vector<T> findAll() noexcept(false);
void findAll(const MultipleRowsCallback &rcb,
const ExceptionCallback &ecb) noexcept;
std::future<std::vector<T>> findFutureAll() noexcept;
//find one
//find by conditions
//insert
//update
//update all
//count
//limit/offset/orderby
//...
private:
Dbclient &_client;
std::string getSqlForFindingByPrimaryKey();
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,39 @@
/**
*
* PgClient.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.
*
* Definitions for the PostgreSQL client class
*
*/
#include <drogon/orm/DbClient.h>
#include <memory>
namespace drogon
{
namespace orm
{
class PgClientImpl;
class PgClient : public DbClient
{
public:
PgClient(const std::string &connInfo, const size_t connNum);
private:
virtual void execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exptCallback) override;
std::shared_ptr<PgClientImpl> _clientPtr;
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,105 @@
/**
*
* Result.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.
*
* @section DESCRIPTION
*
*/
//Taken from libpqxx and modified.
//The license for libpqxx can be found in the COPYING file.
#pragma once
#include <memory>
#include <string>
#ifdef _WIN32
#define noexcept _NOEXCEPT
#endif
namespace drogon
{
namespace orm
{
class ConstResultIterator;
class ConstReverseResultIterator;
class Row;
class ResultImpl;
typedef std::shared_ptr<ResultImpl> ResultImplPtr;
enum class SqlStatus
{
Ok,
End
};
class Result
{
public:
Result(const ResultImplPtr &ptr)
: _resultPtr(ptr) {}
using difference_type = long;
using size_type = unsigned long;
using reference = Row;
using ConstIterator = ConstResultIterator;
using Iterator = ConstIterator;
using row_size_type = unsigned long;
using field_size_type = unsigned long;
using ConstReverseIterator = ConstReverseResultIterator;
using ReverseIterator = ConstReverseIterator;
size_type size() const noexcept;
size_type capacity() const noexcept { return size(); }
ConstIterator begin() const noexcept;
ConstIterator cbegin() const noexcept;
ConstIterator end() const noexcept;
ConstIterator cend() const noexcept;
ConstReverseIterator rbegin() const;
ConstReverseIterator crbegin() const;
ConstReverseIterator rend() const;
ConstReverseIterator crend() const;
bool empty() const noexcept { return size() == 0; }
reference front() const noexcept;
reference back() const noexcept;
reference operator[](size_type index) const;
reference at(size_type index) const;
void swap(Result &) noexcept;
row_size_type columns() const noexcept;
/// Name of column with this number (throws exception if it doesn't exist)
const char *columnName(row_size_type number) const;
const std::string &errorDescription() const { return _errString; }
void setError(const std::string &description) { _errString = description; }
size_type affectedRows() const noexcept;
private:
ResultImplPtr _resultPtr;
std::string _query;
std::string _errString;
friend class Field;
friend class Row;
/// Number of given column (throws exception if it doesn't exist).
row_size_type columnNumber(const char colName[]) const;
/// Number of given column (throws exception if it doesn't exist).
row_size_type columnNumber(const std::string &name) const
{
return columnNumber(name.c_str());
}
const char *getValue(size_type row, row_size_type column) const;
bool isNull(size_type row, row_size_type column) const;
field_size_type getLength(size_type row, row_size_type column) const;
protected:
Result() {}
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,168 @@
/**
*
* ResultIterator.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.
*
* @section DESCRIPTION
*
*/
//Taken from libpqxx and modified.
//The license for libpqxx can be found in the COPYING file.
#pragma once
#include <drogon/orm/Result.h>
#include <drogon/orm/Row.h>
namespace drogon
{
namespace orm
{
class ConstResultIterator
: public std::iterator<
std::random_access_iterator_tag,
const Row,
Result::difference_type,
ConstResultIterator,
Row>,
protected Row
{
public:
using pointer = const Row *;
using reference = Row;
using size_type = Result::size_type;
using difference_type = Result::difference_type;
//ConstResultIterator(const Row &t) noexcept : Row(t) {}
pointer operator->() { return this; }
reference operator*() { return Row(*this); }
ConstResultIterator operator++(int);
ConstResultIterator &operator++()
{
++_index;
return *this;
}
ConstResultIterator operator--(int);
ConstResultIterator &operator--()
{
--_index;
return *this;
}
ConstResultIterator &operator+=(difference_type i)
{
_index += i;
return *this;
}
ConstResultIterator &operator-=(difference_type i)
{
_index -= i;
return *this;
}
bool operator==(const ConstResultIterator &other) const
{
return _index == other._index;
}
bool operator!=(const ConstResultIterator &other) const
{
return _index != other._index;
}
bool operator>(const ConstResultIterator &other) const
{
return _index > other._index;
}
bool operator<(const ConstResultIterator &other) const
{
return _index < other._index;
}
bool operator>=(const ConstResultIterator &other) const
{
return _index >= other._index;
}
bool operator<=(const ConstResultIterator &other) const
{
return _index <= other._index;
}
private:
friend class Result;
ConstResultIterator(const Result &r, size_type index) noexcept : Row(r, index) {}
};
class ConstReverseResultIterator : private ConstResultIterator
{
public:
using super = ConstResultIterator;
using iterator_type = ConstResultIterator;
using iterator_type::difference_type;
using iterator_type::iterator_category;
using iterator_type::pointer;
using value_type = iterator_type::value_type;
using reference = iterator_type::reference;
ConstReverseResultIterator(
const ConstReverseResultIterator &rhs) : ConstResultIterator(rhs) {}
explicit ConstReverseResultIterator(
const ConstResultIterator &rhs) : ConstResultIterator(rhs) { super::operator--(); }
ConstResultIterator base() const noexcept;
using iterator_type::operator->;
using iterator_type::operator*;
ConstReverseResultIterator operator++(int);
ConstReverseResultIterator &operator++()
{
iterator_type::operator--();
return *this;
}
ConstReverseResultIterator operator--(int);
ConstReverseResultIterator &operator--()
{
iterator_type::operator++();
return *this;
}
ConstReverseResultIterator &operator+=(difference_type i)
{
iterator_type::operator-=(i);
return *this;
}
ConstReverseResultIterator &operator-=(difference_type i)
{
iterator_type::operator+=(i);
return *this;
}
bool operator==(const ConstReverseResultIterator &other) const
{
return _index == other._index;
}
bool operator!=(const ConstReverseResultIterator &other) const
{
return _index != other._index;
}
bool operator>(const ConstReverseResultIterator &other) const
{
return _index < other._index;
}
bool operator<(const ConstReverseResultIterator &other) const
{
return _index > other._index;
}
bool operator>=(const ConstReverseResultIterator &other) const
{
return _index <= other._index;
}
bool operator<=(const ConstReverseResultIterator &other) const
{
return _index >= other._index;
}
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,73 @@
/**
*
* Row.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.
*
* @section DESCRIPTION
*
*/
//Taken from libpqxx and modified.
//The license for libpqxx can be found in the COPYING file.
#pragma once
#include <drogon/orm/Result.h>
#include <string>
namespace drogon
{
namespace orm
{
class Field;
class ConstRowIterator;
class ConstReverseRowIterator;
class Row
{
public:
using size_type = unsigned long;
using reference = Field;
using ConstIterator = ConstRowIterator;
using Iterator = ConstIterator;
using ConstReverseIterator = ConstReverseRowIterator;
using difference_type = long;
reference operator[](size_type index) const;
reference operator[](const char columnName[]) const;
reference operator[](const std::string &columnName) const;
size_type size() const;
size_type capacity() const noexcept { return size(); }
ConstIterator begin() const noexcept;
ConstIterator cbegin() const noexcept;
ConstIterator end() const noexcept;
ConstIterator cend() const noexcept;
ConstReverseIterator rbegin() const;
ConstReverseIterator crbegin() const;
ConstReverseIterator rend() const;
ConstReverseIterator crend() const;
private:
Result _result;
protected:
friend class Field;
/**
* Row number
* You'd expect this to be a size_t, but due to the way reverse iterators
* are related to regular iterators, it must be allowed to underflow to -1.
*/
long _index = 0;
size_t _end = 0;
friend class Result;
Row(const Result &r, size_type index) noexcept;
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,167 @@
/**
*
* RowIterator.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.
*
* @section DESCRIPTION
*
*/
//Taken from libpqxx and modified.
//The license for libpqxx can be found in the COPYING file.
#pragma once
#include <drogon/orm/Row.h>
#include <drogon/orm/Field.h>
namespace drogon
{
namespace orm
{
class ConstRowIterator : public std::iterator<
std::random_access_iterator_tag,
const Field,
Row::difference_type,
ConstRowIterator,
Field>,
protected Field
{
public:
using pointer = const Field *;
using reference = Field;
using size_type = Row::size_type;
using difference_type = Row::difference_type;
//ConstRowIterator(const Field &t) noexcept : Field(t) {}
pointer operator->() { return this; }
reference operator*() { return Field(*this); }
ConstRowIterator operator++(int);
ConstRowIterator &operator++()
{
++_column;
return *this;
}
ConstRowIterator operator--(int);
ConstRowIterator &operator--()
{
--_column;
return *this;
}
ConstRowIterator &operator+=(difference_type i)
{
_column += i;
return *this;
}
ConstRowIterator &operator-=(difference_type i)
{
_column -= i;
return *this;
}
bool operator==(const ConstRowIterator &other) const
{
return _column == other._column;
}
bool operator!=(const ConstRowIterator &other) const
{
return _column != other._column;
}
bool operator>(const ConstRowIterator &other) const
{
return _column > other._column;
}
bool operator<(const ConstRowIterator &other) const
{
return _column < other._column;
}
bool operator>=(const ConstRowIterator &other) const
{
return _column >= other._column;
}
bool operator<=(const ConstRowIterator &other) const
{
return _column <= other._column;
}
private:
friend class Row;
ConstRowIterator(const Row &r, size_type column) noexcept : Field(r, column) {}
};
class ConstReverseRowIterator : private ConstRowIterator
{
public:
using super = ConstRowIterator;
using iterator_type = ConstRowIterator;
using iterator_type::difference_type;
using iterator_type::iterator_category;
using iterator_type::pointer;
using value_type = iterator_type::value_type;
using reference = iterator_type::reference;
ConstReverseRowIterator(
const ConstReverseRowIterator &rhs) : ConstRowIterator(rhs) {}
explicit ConstReverseRowIterator(
const ConstRowIterator &rhs) : ConstRowIterator(rhs) { super::operator--(); }
ConstRowIterator base() const noexcept;
using iterator_type::operator->;
using iterator_type::operator*;
ConstReverseRowIterator operator++(int);
ConstReverseRowIterator &operator++()
{
iterator_type::operator--();
return *this;
}
ConstReverseRowIterator operator--(int);
ConstReverseRowIterator &operator--()
{
iterator_type::operator++();
return *this;
}
ConstReverseRowIterator &operator+=(difference_type i)
{
iterator_type::operator-=(i);
return *this;
}
ConstReverseRowIterator &operator-=(difference_type i)
{
iterator_type::operator+=(i);
return *this;
}
bool operator==(const ConstReverseRowIterator &other) const
{
return _column == other._column;
}
bool operator!=(const ConstReverseRowIterator &other) const
{
return _column != other._column;
}
bool operator>(const ConstReverseRowIterator &other) const
{
return _column < other._column;
}
bool operator<(const ConstReverseRowIterator &other) const
{
return _column > other._column;
}
bool operator>=(const ConstReverseRowIterator &other) const
{
return _column <= other._column;
}
bool operator<=(const ConstReverseRowIterator &other) const
{
return _column >= other._column;
}
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,278 @@
/**
*
* SqlBinder.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.
*
*
*/
#pragma once
#include <drogon/orm/Row.h>
#include <drogon/orm/Field.h>
#include <drogon/orm/ResultIterator.h>
#include <drogon/orm/RowIterator.h>
#include <drogon/orm/FunctionTraits.h>
#include <drogon/orm/Exception.h>
#include <trantor/utils/Logger.h>
#include <string.h>
#include <string>
#include <iostream>
#include <functional>
#include <map>
#include <vector>
namespace drogon
{
namespace orm
{
class DbClient;
typedef std::function<void(const Result &)> QueryCallback;
typedef std::function<void(const std::exception_ptr &)> ExceptCallback;
enum class Mode
{
NonBlocking,
Blocking
};
namespace internal
{
//we only accept value type or const lreference type or rreference type as handle method parameters type
template <typename T>
struct CallbackArgTypeTraits
{
static const bool isValid = true;
};
template <typename T>
struct CallbackArgTypeTraits<T *>
{
static const bool isValid = false;
};
template <typename T>
struct CallbackArgTypeTraits<T &>
{
static const bool isValid = false;
};
template <typename T>
struct CallbackArgTypeTraits<T &&>
{
static const bool isValid = true;
};
template <typename T>
struct CallbackArgTypeTraits<const T &>
{
static const bool isValid = true;
};
class CallbackHolderBase
{
public:
virtual ~CallbackHolderBase()
{
}
virtual void execCallback(const Result &result) = 0;
};
template <typename Function>
class CallbackHolder : public CallbackHolderBase
{
public:
virtual void execCallback(const Result &result)
{
run(result);
}
CallbackHolder(Function &&function) : _function(std::forward<Function>(function))
{
static_assert(traits::isSqlCallback, "Your sql callback function type is wrong!");
}
private:
Function _function;
typedef FunctionTraits<Function> traits;
template <
std::size_t Index>
using NthArgumentType = typename traits::template argument<Index>;
static const size_t argumentCount = traits::arity;
template <bool isStep = traits::isStepResultCallback>
typename std::enable_if<isStep, void>::type run(const Result &result)
{
if (result.size() == 0)
{
run(nullptr, true);
return;
}
for (auto row : result)
{
run(&row, false);
}
run(nullptr, true);
}
template <bool isStep = traits::isStepResultCallback>
typename std::enable_if<!isStep, void>::type run(const Result &result)
{
static_assert(argumentCount == 0, "Your sql callback function type is wrong!");
_function(result);
}
template <
typename... Values,
std::size_t Boundary = argumentCount>
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<NthArgumentType<sizeof...(Values)>>::isValid,
"your sql callback function argument type must be value type or const left-reference type");
typedef typename std::remove_cv<typename std::remove_reference<NthArgumentType<sizeof...(Values)>>::type>::type ValueType;
ValueType value = ValueType();
if (row && row->size() > sizeof...(Values))
{
value = (*row)[sizeof...(Values)].as<ValueType>();
}
run(row, isNull, std::forward<Values>(values)..., std::move(value));
}
template <
typename... Values,
std::size_t Boundary = argumentCount>
typename std::enable_if<(sizeof...(Values) == Boundary), void>::type run(
const Row *const row,
bool isNull,
Values &&... values)
{
_function(isNull, std::move(values)...);
}
};
class SqlBinder
{
typedef SqlBinder self;
public:
friend class Dbclient;
SqlBinder(const std::string &sql, DbClient &client) : _sql(sql), _client(client)
{
}
~SqlBinder();
template <typename CallbackType,
typename traits = FunctionTraits<CallbackType>>
typename std::enable_if<traits::isExceptCallback && traits::isPtr, self>::type &operator>>(CallbackType &&callback)
{
LOG_DEBUG << "ptr callback";
_isExceptPtr = true;
_exceptPtrCallback = std::forward<CallbackType>(callback);
return *this;
}
template <typename CallbackType,
typename traits = FunctionTraits<CallbackType>>
typename std::enable_if<traits::isExceptCallback && !traits::isPtr, self>::type &operator>>(CallbackType &&callback)
{
_isExceptPtr = false;
_exceptCallback = std::forward<CallbackType>(callback);
return *this;
}
template <typename CallbackType,
typename traits = FunctionTraits<CallbackType>>
typename std::enable_if<traits::isSqlCallback, self>::type &operator>>(CallbackType &&callback)
{
_callbackHolder = std::shared_ptr<CallbackHolderBase>(new CallbackHolder<CallbackType>(std::forward<CallbackType>(callback)));
return *this;
}
template <typename T>
self &operator<<(T &&parameter)
{
_paraNum++;
typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type ParaType;
std::shared_ptr<void> obj =
std::make_shared<ParaType>(parameter);
switch (sizeof(T))
{
case 2:
*std::static_pointer_cast<short>(obj) = ntohs(parameter);
break;
case 4:
*std::static_pointer_cast<int>(obj) = ntohl(parameter);
break;
case 8:
*std::static_pointer_cast<long>(obj) = ntohll(parameter);
break;
case 1:
default:
break;
}
_objs.push_back(obj);
_parameters.push_back((char *)obj.get());
_length.push_back(sizeof(T));
_format.push_back(1);
return *this;
}
//template <>
self &operator<<(const char str[])
{
_paraNum++;
_parameters.push_back((char *)str);
_length.push_back(strlen(str));
_format.push_back(0);
return *this;
}
self &operator<<(char str[])
{
return operator<<((const char *)str);
}
self &operator<<(const std::string &str)
{
_paraNum++;
_parameters.push_back((char *)str.c_str());
_length.push_back(str.length());
_format.push_back(0);
return *this;
}
self &operator<<(std::string &str)
{
return operator<<((const std::string &)str);
}
self &operator<<(std::string &&str)
{
return operator<<((const std::string &)str);
}
self &operator<<(const Mode &mode)
{
_mode = mode;
return *this;
}
self &operator<<(Mode &&mode)
{
_mode = mode;
return *this;
}
void exec() noexcept(false);
private:
std::string _sql;
DbClient &_client;
size_t _paraNum = 0;
std::vector<const char *> _parameters;
std::vector<int> _length;
std::vector<int> _format;
std::vector<std::shared_ptr<void>> _objs;
Mode _mode = Mode::NonBlocking;
std::shared_ptr<CallbackHolderBase> _callbackHolder;
DrogonDbExceptionCallback _exceptCallback;
ExceptCallback _exceptPtrCallback;
bool _execed = false;
bool _destructed = false;
bool _isExceptPtr = false;
};
} // namespace internal
} // namespace orm
} // namespace drogon

21
orm_lib/src/DbClient.cc Normal file
View File

@ -0,0 +1,21 @@
/**
*
* DbClient.cc
* 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 <drogon/orm/DbClient.h>
using namespace drogon::orm;
using namespace drogon;
internal::SqlBinder DbClient::operator << (const std::string &sql)
{
return internal::SqlBinder(sql,*this);
}

114
orm_lib/src/Exception.cc Normal file
View File

@ -0,0 +1,114 @@
/**
*
* Copyright (c) 2005-2017, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this mistake,
* or contact the author.
*/
//taken from libpqxx and modified
/**
*
* Exception.cc
* 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 <drogon/orm/Exception.h>
using namespace drogon::orm;
DrogonDbException::~DrogonDbException() noexcept
{
}
Failure::Failure(const std::string &whatarg) : std::runtime_error(whatarg)
{
}
BrokenConnection::BrokenConnection() : Failure("Connection to database failed")
{
}
BrokenConnection::BrokenConnection(const std::string &whatarg) : Failure(whatarg)
{
}
SqlError::SqlError(
const std::string &whatarg,
const std::string &Q,
const char sqlstate[]) : Failure(whatarg),
_query(Q),
_sqlState(sqlstate ? sqlstate : "")
{
}
SqlError::~SqlError() noexcept
{
}
const std::string &SqlError::query() const noexcept
{
return _query;
}
const std::string &SqlError::sqlState() const noexcept
{
return _sqlState;
}
InDoubtError::InDoubtError(const std::string &whatarg)
: Failure(whatarg)
{
}
TransactionRollback::TransactionRollback(const std::string &whatarg)
: Failure(whatarg)
{
}
SerializationFailure::SerializationFailure(const std::string &whatarg)
: TransactionRollback(whatarg)
{
}
StatementCompletionUnknown::StatementCompletionUnknown(const std::string &whatarg)
: TransactionRollback(whatarg)
{
}
DeadlockDetected::DeadlockDetected(const std::string &whatarg)
: TransactionRollback(whatarg)
{
}
InternalError::InternalError(const std::string &whatarg)
: logic_error("libpqxx internal error: " + whatarg)
{
}
UsageError::UsageError(const std::string &whatarg)
: logic_error(whatarg)
{
}
ArgumentError::ArgumentError(const std::string &whatarg)
: invalid_argument(whatarg)
{
}
ConversionError::ConversionError(const std::string &whatarg)
: domain_error(whatarg)
{
}
RangeError::RangeError(const std::string &whatarg)
: out_of_range(whatarg)
{
}

50
orm_lib/src/Field.cc Normal file
View File

@ -0,0 +1,50 @@
/**
*
* Field.cc
* 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 <drogon/orm/Field.h>
using namespace drogon::orm;
Field::Field(const Row &row, Row::size_type columnNum) noexcept
: _row(Result::size_type(row._index)),
_column(columnNum),
_result(row._result)
{
}
const char *Field::name() const
{
return _result.columnName(_column);
}
bool Field::isNull() const
{
return _result.isNull(_row, _column);
}
template <>
std::string Field::as<std::string>() const
{
auto _data = _result.getValue(_row, _column);
auto _dataLength = _result.getLength(_row, _column);
return std::string(_data, _dataLength);
}
template <>
const char *Field::as<const char *>() const
{
auto _data = _result.getValue(_row, _column);
return _data;
}
template <>
char *Field::as<char *>() const
{
auto _data = _result.getValue(_row, _column);
return (char *)_data;
}

145
orm_lib/src/Result.cc Normal file
View File

@ -0,0 +1,145 @@
/**
*
* Result.cc
* 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 "ResultImpl.h"
#include <drogon/orm/Result.h>
#include <drogon/orm/Row.h>
#include <drogon/orm/ResultIterator.h>
#include <assert.h>
using namespace drogon::orm;
Result::ConstIterator Result::begin() const noexcept
{
return ConstIterator(*this, (size_type)0);
}
Result::ConstIterator Result::cbegin() const noexcept
{
return begin();
}
Result::ConstIterator Result::end() const noexcept
{
return ConstIterator(*this, size());
}
Result::ConstIterator Result::cend() const noexcept
{
return end();
}
Result::ConstReverseIterator Result::rbegin() const
{
return ConstReverseResultIterator(end());
}
Result::ConstReverseIterator Result::crbegin() const
{
return rbegin();
}
Result::ConstReverseIterator Result::rend() const
{
return ConstReverseResultIterator(begin());
}
Result::ConstReverseIterator Result::crend() const
{
return rend();
}
Result::ConstIterator Result::ConstReverseIterator::base() const noexcept
{
iterator_type tmp(*this);
return ++tmp;
}
Result::reference Result::front() const noexcept
{
return Row(*this, 0);
}
Result::reference Result::back() const noexcept
{
return Row(*this, size() - 1);
}
Result::reference Result::operator[](size_type index) const
{
assert(index < size());
return Row(*this, index);
}
Result::reference Result::at(size_type index) const
{
return operator[](index);
}
ConstResultIterator ConstResultIterator::operator++(int)
{
ConstResultIterator old(*this);
_index++;
return old;
}
ConstResultIterator ConstResultIterator::operator--(int)
{
ConstResultIterator old(*this);
_index--;
return old;
}
ConstReverseResultIterator ConstReverseResultIterator::operator++(int)
{
ConstReverseResultIterator old(*this);
iterator_type::operator--();
return old;
}
ConstReverseResultIterator ConstReverseResultIterator::operator--(int)
{
ConstReverseResultIterator old(*this);
iterator_type::operator++();
return old;
}
Result::size_type Result::size() const noexcept
{
return _resultPtr->size();
}
void Result::swap(Result &other) noexcept
{
_resultPtr.swap(other._resultPtr);
_query.swap(other._query);
_errString.swap(other._errString);
}
Result::row_size_type Result::columns() const noexcept
{
return _resultPtr->columns();
}
const char *Result::columnName(Result::row_size_type number) const
{
return _resultPtr->columnName(number);
}
Result::size_type Result::affectedRows() const noexcept
{
return _resultPtr->affectedRows();
}
Result::row_size_type Result::columnNumber(const char colName[]) const
{
return _resultPtr->columnNumber(colName);
}
const char *Result::getValue(Result::size_type row, Result::row_size_type column) const
{
return _resultPtr->getValue(row,column);
}
bool Result::isNull(Result::size_type row, Result::row_size_type column) const
{
return _resultPtr->isNull(row,column);
}
Result::field_size_type Result::getLength(Result::size_type row, Result::row_size_type column) const
{
return _resultPtr->getLength(row,column);
}

35
orm_lib/src/ResultImpl.h Normal file
View File

@ -0,0 +1,35 @@
/**
*
* ResultImpl.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 <trantor/utils/NonCopyable.h>
#include <drogon/orm/Result.h>
namespace drogon
{
namespace orm
{
class ResultImpl : public trantor::NonCopyable, public Result
{
public:
virtual size_type size() const noexcept = 0;
virtual row_size_type columns() const noexcept = 0;
virtual const char *columnName(row_size_type Number) const = 0;
virtual size_type affectedRows() const noexcept = 0;
virtual row_size_type columnNumber(const char colName[]) const = 0;
virtual const char *getValue(size_type row, row_size_type column) const = 0;
virtual bool isNull(size_type row, row_size_type column) const = 0;
virtual field_size_type getLength(size_type row, row_size_type column) const = 0;
virtual ~ResultImpl() {}
};
} // namespace orm
} // namespace drogon

97
orm_lib/src/Row.cc Normal file
View File

@ -0,0 +1,97 @@
#include <drogon/orm/Row.h>
#include <drogon/orm/Field.h>
#include <drogon/orm/RowIterator.h>
using namespace drogon::orm;
Row::Row(const Result &r, size_type index) noexcept
: _result(r),
_index(long(index)),
_end(r.columns())
{
}
Row::size_type Row::size() const
{
return _end;
}
Row::reference Row::operator[](size_type index) const
{
if (index >= _end)
throw; //TODO throw....
return Field(*this, index);
}
Row::reference Row::operator[](const char columnName[]) const
{
auto N = _result.columnNumber(columnName);
return Field(*this, N);
}
Row::reference Row::operator[](const std::string &columnName) const
{
return operator[](columnName.c_str());
}
Row::ConstIterator Row::begin() const noexcept
{
return ConstIterator(*this, 0);
}
Row::ConstIterator Row::cbegin() const noexcept
{
return begin();
}
Row::ConstIterator Row::end() const noexcept
{
return ConstIterator(*this, size());
}
Row::ConstIterator Row::cend() const noexcept
{
return end();
}
Row::ConstReverseIterator Row::rbegin() const
{
return ConstReverseRowIterator(end());
}
Row::ConstReverseIterator Row::crbegin() const
{
return rbegin();
}
Row::ConstReverseIterator Row::rend() const
{
return ConstReverseRowIterator(begin());
}
Row::ConstReverseIterator Row::crend() const
{
return rend();
}
Row::ConstIterator Row::ConstReverseIterator::base() const noexcept
{
iterator_type tmp(*this);
return ++tmp;
}
ConstRowIterator ConstRowIterator::operator++(int)
{
ConstRowIterator old(*this);
_column++;
return old;
}
ConstRowIterator ConstRowIterator::operator--(int)
{
ConstRowIterator old(*this);
_column--;
return old;
}
ConstReverseRowIterator ConstReverseRowIterator::operator++(int)
{
ConstReverseRowIterator old(*this);
iterator_type::operator--();
return old;
}
ConstReverseRowIterator ConstReverseRowIterator::operator--(int)
{
ConstReverseRowIterator old(*this);
iterator_type::operator++();
return old;
}

116
orm_lib/src/SqlBinder.cc Normal file
View File

@ -0,0 +1,116 @@
/**
*
* SqlBinder.cc
* 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 <drogon/config.h>
#include <drogon/orm/SqlBinder.h>
#include <drogon/orm/DbClient.h>
#include <stdio.h>
#include <future>
#include <iostream>
using namespace drogon::orm;
using namespace drogon::orm::internal;
void SqlBinder::exec()
{
_execed = true;
if (_mode == Mode::NonBlocking)
{
//nonblocking mode,default mode
auto holder = std::move(_callbackHolder);
auto exceptCb = std::move(_exceptCallback);
auto exceptPtrCb = std::move(_exceptPtrCallback);
auto isExceptPtr = _isExceptPtr;
_client.execSql(_sql, _paraNum, _parameters, _length, _format,
[holder](const Result &r) {
if (holder)
{
holder->execCallback(r);
}
},
[exceptCb, exceptPtrCb, isExceptPtr](const std::exception_ptr &exception) {
//LOG_DEBUG<<"exp callback "<<isExceptPtr;
if (!isExceptPtr)
{
if (exceptCb)
{
try
{
std::rethrow_exception(exception);
}
catch (const DrogonDbException &e)
{
exceptCb(e);
}
}
}
else
{
if (exceptPtrCb)
exceptPtrCb(exception);
}
});
}
else
{
//blocking mode
std::shared_ptr<std::promise<Result>> pro(new std::promise<Result>);
auto f = pro->get_future();
_client.execSql(_sql, _paraNum, _parameters, _length, _format,
[pro](const Result &r) {
pro->set_value(r);
},
[pro](const std::exception_ptr &exception) {
try
{
pro->set_exception(exception);
}
catch (...)
{
assert(0);
}
});
if (_callbackHolder || _exceptCallback)
{
try
{
const Result &v = f.get();
if (_callbackHolder)
{
_callbackHolder->execCallback(v);
}
}
catch (const DrogonDbException &exception)
{
if (!_destructed)
{
//throw exception
std::rethrow_exception(std::current_exception());
}
else
{
if (_exceptCallback)
{
_exceptCallback(exception);
}
}
}
}
}
}
SqlBinder::~SqlBinder()
{
_destructed = true;
if (!_execed)
{
exec();
}
}

View File

@ -0,0 +1,243 @@
#include "DbConnection.h"
#include "PostgreSQLResultImpl.h"
#include <drogon/orm/Exception.h>
#include <stdio.h>
using namespace drogon::orm;
namespace drogon
{
namespace orm
{
Result makeResult(const std::shared_ptr<PGresult> &r = std::shared_ptr<PGresult>(nullptr), const std::string &query = "")
{
return Result(std::shared_ptr<PostgreSQLResultImpl>(new PostgreSQLResultImpl(r, query)));
}
} // namespace orm
} // namespace drogon
DbConnection::DbConnection(trantor::EventLoop *loop, const std::string &connInfo)
: _connPtr(std::shared_ptr<PGconn>(PQconnectStart(connInfo.c_str()), [](PGconn *conn) {
PQfinish(conn);
})),
_loop(loop), _channel(_loop, PQsocket(_connPtr.get()))
{
//std::cout<<"sock="<<sock()<<std::endl;
_channel.setReadCallback([=]() {
//std::cout<<"reading callback"<<std::endl;
if (_status != ConnectStatus_Ok)
{
pgPoll();
}
else
{
handleRead();
}
});
_channel.setWriteCallback([=]() {
//std::cout<<"writing callback"<<std::endl;
if (_status != ConnectStatus_Ok)
{
pgPoll();
}
else
{
PQconsumeInput(_connPtr.get());
}
});
_channel.setCloseCallback([=]() {
perror("sock close");
handleClosed();
});
_channel.setErrorCallback([=]() {
perror("sock err");
handleClosed();
});
_channel.enableReading();
_channel.enableWriting();
}
int DbConnection::sock()
{
return PQsocket(_connPtr.get());
}
void DbConnection::handleClosed()
{
std::cout << "handleClosed!" << this << std::endl;
_loop->assertInLoopThread();
_channel.disableAll();
_channel.remove();
assert(_closeCb);
auto thisPtr = shared_from_this();
_closeCb(thisPtr);
}
void DbConnection::pgPoll()
{
_loop->assertInLoopThread();
auto connStatus = PQconnectPoll(_connPtr.get());
switch (connStatus)
{
case PGRES_POLLING_FAILED:
/* fprintf(stderr, "!!!Pg connection failed: %s",
PQerrorMessage(_connPtr.get()));
if(_isWorking){
_isWorking=false;
auto r=makeResult(SqlStatus::NetworkError, nullptr,_sql);
r.setError(PQerrorMessage(_connPtr.get()));
assert(_cb);
_cb(r);
}
handleClosed();
*/
fprintf(stderr, "!!!Pg connection failed: %s",
PQerrorMessage(_connPtr.get()));
break;
case PGRES_POLLING_WRITING:
_channel.enableWriting();
_channel.disableReading();
break;
case PGRES_POLLING_READING:
_channel.enableReading();
_channel.disableWriting();
break;
case PGRES_POLLING_OK:
if (_status != ConnectStatus_Ok)
{
_status = ConnectStatus_Ok;
assert(_okCb);
_okCb(shared_from_this());
}
_channel.enableReading();
_channel.disableWriting();
break;
case PGRES_POLLING_ACTIVE:
//unused!
printf("active\n");
break;
default:
break;
}
}
void DbConnection::execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback,
const std::function<void()> &idleCb)
{
assert(paraNum == parameters.size());
assert(paraNum == length.size());
assert(paraNum == format.size());
assert(rcb);
assert(idleCb);
assert(!_isWorking);
assert(!sql.empty());
_sql = sql;
_cb = rcb;
_idleCb = idleCb;
_isWorking = true;
_exceptCb = exceptCallback;
//_channel.enableWriting();
if (PQsendQueryParams(
_connPtr.get(),
sql.c_str(),
paraNum,
NULL,
parameters.data(),
length.data(),
format.data(),
1) == 0)
{
fprintf(stderr, "send query error:%s\n", PQerrorMessage(_connPtr.get()));
//FIXME call exception callback
}
auto thisPtr = shared_from_this();
_loop->runInLoop([=]() {
thisPtr->pgPoll();
});
}
void DbConnection::handleRead()
{
std::shared_ptr<PGresult> res;
if (!PQconsumeInput(_connPtr.get()))
{
fprintf(stderr, "Failed to consume pg input: %s\n",
PQerrorMessage(_connPtr.get()));
if (_isWorking)
{
_isWorking = false;
try
{
throw BrokenConnection(PQerrorMessage(_connPtr.get()));
}
catch (...)
{
auto exceptPtr = std::current_exception();
_exceptCb(exceptPtr);
_exceptCb = decltype(_exceptCb)();
}
_cb = decltype(_cb)();
}
handleClosed();
return;
}
if (PQisBusy(_connPtr.get()))
{
//need read more data from socket;
printf("need read more data from socket!\n");
return;
}
_channel.disableWriting();
// got query results?
while ((res = std::shared_ptr<PGresult>(PQgetResult(_connPtr.get()), [](PGresult *p) {
PQclear(p);
})))
{
auto type = PQresultStatus(res.get());
if (type == PGRES_BAD_RESPONSE || type == PGRES_FATAL_ERROR)
{
fprintf(stderr, "Result error: %s", PQerrorMessage(_connPtr.get()));
if (_isWorking)
{
{
try
{
//FIXME exception type
throw SqlError(PQerrorMessage(_connPtr.get()),
_sql);
}
catch (...)
{
_exceptCb(std::current_exception());
_exceptCb = decltype(_exceptCb)();
}
}
_cb = decltype(_cb)();
}
}
else
{
if (_isWorking)
{
auto r = makeResult(res, _sql);
_cb(r);
_cb = decltype(_cb)();
_exceptCb = decltype(_exceptCb)();
}
}
}
if (_isWorking)
{
_isWorking = false;
_idleCb();
}
}

View File

@ -0,0 +1,74 @@
#pragma once
#include <trantor/net/EventLoop.h>
#include <trantor/net/inner/Channel.h>
#include <drogon/orm/DbClient.h>
#include <trantor/utils/NonCopyable.h>
#include <libpq-fe.h>
#include <memory>
#include <string>
#include <functional>
#include <iostream>
namespace drogon
{
namespace orm
{
enum ConnectStatus
{
ConnectStatus_None = 0,
ConnectStatus_Connecting,
ConnectStatus_Ok,
ConnectStatus_Bad
};
class DbConnection;
typedef std::shared_ptr<DbConnection> DbConnectionPtr;
class DbConnection : public trantor::NonCopyable, public std::enable_shared_from_this<DbConnection>
{
public:
typedef std::function<void(const DbConnectionPtr &)> DbConnectionCallback;
DbConnection(trantor::EventLoop *loop, const std::string &connInfo);
void setOkCallback(const DbConnectionCallback &cb)
{
_okCb = cb;
}
void setCloseCallback(const DbConnectionCallback &cb)
{
_closeCb = cb;
}
void execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback,
const std::function<void()> &idleCb);
~DbConnection()
{
//std::cout<<"unconstruct DbConn"<<this<<std::endl;
}
int sock();
private:
std::shared_ptr<PGconn> _connPtr;
trantor::EventLoop *_loop;
trantor::Channel _channel;
QueryCallback _cb;
std::function<void()> _idleCb;
ConnectStatus _status = ConnectStatus_None;
DbConnectionCallback _closeCb = [](const DbConnectionPtr &) {};
DbConnectionCallback _okCb = [](const DbConnectionPtr &) {};
std::function<void(const std::exception_ptr &)> _exceptCb;
bool _isWorking = false;
std::string _sql = "";
void handleRead();
void pgPoll();
void handleClosed();
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,49 @@
/**
*
* PgClient.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.
*
* Implementation of the drogon::orm::PgClient class.
*
*/
#include "PgClientImpl.h"
#include <drogon/orm/PgClient.h>
#include <drogon/orm/Exception.h>
#include <thread>
#include <unistd.h>
using namespace drogon::orm;
void PgClient::execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &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))
{
}

View File

@ -0,0 +1,202 @@
//
// Created by antao on 2018/6/22.
//
#include "PgClientImpl.h"
#include "DbConnection.h"
#include <trantor/net/EventLoop.h>
#include <trantor/net/inner/Channel.h>
#include <drogon/orm/Exception.h>
#include <drogon/orm/DbClient.h>
#include <libpq-fe.h>
#include <sys/select.h>
#include <iostream>
#include <thread>
#include <vector>
#include <unordered_set>
#include <memory>
#include <stdio.h>
#include <unistd.h>
using namespace drogon::orm;
DbConnectionPtr PgClientImpl::newConnection(trantor::EventLoop *loop)
{
//std::cout<<"newConn"<<std::endl;
auto connPtr = std::make_shared<DbConnection>(loop, _connInfo);
connPtr->setCloseCallback([=](const DbConnectionPtr &closeConnPtr) {
//std::cout<<"Conn closed!"<<closeConnPtr<<std::endl;
sleep(1);
std::lock_guard<std::mutex> guard(_connectionsMutex);
_readyConnections.erase(closeConnPtr);
_busyConnections.erase(closeConnPtr);
assert(_connections.find(closeConnPtr) != _connections.end());
_connections.erase(closeConnPtr);
_connections.insert(newConnection(loop));
//std::cout<<"Conn closed!end"<<std::endl;
});
connPtr->setOkCallback([=](const DbConnectionPtr &okConnPtr) {
LOG_TRACE << "postgreSQL connected!" ;
std::lock_guard<std::mutex> guard(_connectionsMutex);
_readyConnections.insert(okConnPtr);
});
//std::cout<<"newConn end"<<connPtr<<std::endl;
return connPtr;
}
PgClientImpl::PgClientImpl(const std::string &connInfo, const size_t connNum)
: _connInfo(connInfo),
_connectNum(connNum)
{
assert(connNum > 0);
_loopThread = std::thread([=]() {
_loopPtr = std::unique_ptr<trantor::EventLoop>(new trantor::EventLoop);
ioLoop();
});
}
void PgClientImpl::ioLoop()
{
for (size_t i = 0; i < _connectNum; i++)
{
_connections.insert(newConnection(_loopPtr.get()));
}
_loopPtr->loop();
}
PgClientImpl::~PgClientImpl()
{
_stop = true;
_loopPtr->quit();
if (_loopThread.joinable())
_loopThread.join();
}
void PgClientImpl::execSql(const DbConnectionPtr &conn,
const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &cb,
const std::function<void(const std::exception_ptr &)> &exceptCallback)
{
if (!conn)
{
try
{
throw BrokenConnection("There is no connection to PG server!");
}
catch (...)
{
exceptCallback(std::current_exception());
}
return;
}
std::weak_ptr<DbConnection> weakConn = conn;
conn->execSql(sql, paraNum, parameters, length, format,
cb, exceptCallback,
[=]() -> void {
{
auto connPtr = weakConn.lock();
if (!connPtr)
return;
{
std::lock_guard<std::mutex> guard(_bufferMutex);
if (_sqlCmdBuffer.size() > 0)
{
auto cmd = _sqlCmdBuffer.front();
_sqlCmdBuffer.pop_front();
_loopPtr->queueInLoop([=]() {
std::vector<const char *> paras;
std::vector<int> lens;
for (auto p : cmd._parameters)
{
paras.push_back(p.c_str());
lens.push_back(p.length());
}
execSql(connPtr, cmd._sql, cmd._paraNum, paras, lens, cmd._format, cmd._cb, cmd._exceptCb);
});
return;
}
}
std::lock_guard<std::mutex> guard(_connectionsMutex);
_busyConnections.erase(connPtr);
_readyConnections.insert(connPtr);
}
});
}
void PgClientImpl::execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const QueryCallback &cb,
const ExceptCallback &exceptCb)
{
assert(paraNum == parameters.size());
assert(paraNum == length.size());
assert(paraNum == format.size());
assert(cb);
DbConnectionPtr conn;
{
std::lock_guard<std::mutex> guard(_connectionsMutex);
if (_readyConnections.size() == 0)
{
if (_busyConnections.size() == 0)
{
//std::cout<<"no connection"<<std::endl;
//FIXME call exception callback
try
{
throw BrokenConnection("No connection to postgreSQL server");
}
catch(...)
{
exceptCb(std::current_exception());
}
return;
}
}
else
{
auto iter = _readyConnections.begin();
_busyConnections.insert(*iter);
conn = *iter;
_readyConnections.erase(iter);
}
}
if (conn)
{
execSql(conn, sql, paraNum, parameters, length, format, cb, exceptCb);
return;
}
bool busy = false;
{
std::lock_guard<std::mutex> guard(_bufferMutex);
if (_sqlCmdBuffer.size() > 10000)
{
//too many queries in buffer;
busy = true;
}
}
if (busy)
{
//FIXME call except callback
return;
}
SqlCmd cmd;
cmd._sql = sql;
cmd._paraNum = paraNum;
for (size_t i = 0; i < parameters.size(); i++)
{
cmd._parameters.push_back(std::string(parameters[i], length[i]));
}
cmd._format = format;
cmd._cb = cb;
cmd._exceptCb = exceptCb;
{
std::lock_guard<std::mutex> guard(_bufferMutex);
_sqlCmdBuffer.push_back(std::move(cmd));
}
}

View File

@ -0,0 +1,76 @@
#pragma once
#include "DbConnection.h"
#include <drogon/orm/DbClient.h>
#include <trantor/net/EventLoop.h>
#include <memory>
#include <thread>
#include <functional>
#include <string>
#include <unordered_set>
#include <list>
namespace drogon
{
namespace orm
{
// extern Result makeResult(SqlStatus status, const std::shared_ptr<PGresult> &r = std::shared_ptr<PGresult>(nullptr),
// const std::string &query = "");
class PgClientImpl : public trantor::NonCopyable
{
public:
PgClientImpl(const std::string &connInfo, const size_t connNum);
~PgClientImpl();
void execSql(const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback);
private:
void ioLoop();
std::unique_ptr<trantor::EventLoop> _loopPtr;
enum ConnectStatus
{
ConnectStatus_None = 0,
ConnectStatus_Connecting,
ConnectStatus_Ok,
ConnectStatus_Bad
};
void execSql(const DbConnectionPtr &conn, const std::string &sql,
size_t paraNum,
const std::vector<const char *> &parameters,
const std::vector<int> &length,
const std::vector<int> &format,
const ResultCallback &rcb,
const std::function<void(const std::exception_ptr &)> &exceptCallback);
DbConnectionPtr newConnection(trantor::EventLoop *loop);
std::unordered_set<DbConnectionPtr> _connections;
std::unordered_set<DbConnectionPtr> _readyConnections;
std::unordered_set<DbConnectionPtr> _busyConnections;
std::string _connInfo;
std::thread _loopThread;
std::mutex _connectionsMutex;
size_t _connectNum;
bool _stop = false;
struct SqlCmd
{
std::string _sql;
size_t _paraNum;
std::vector<std::string> _parameters;
std::vector<int> _format;
QueryCallback _cb;
ExceptCallback _exceptCb;
};
std::list<SqlCmd> _sqlCmdBuffer;
std::mutex _bufferMutex;
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,68 @@
/**
*
* PostgreSQLResultImpl.cc
* 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 "PostgreSQLResultImpl.h"
#include <assert.h>
using namespace drogon::orm;
Result::size_type PostgreSQLResultImpl::size() const noexcept
{
return _result ? PQntuples(_result.get()) : 0;
}
Result::row_size_type PostgreSQLResultImpl::columns() const noexcept
{
auto ptr = const_cast<PGresult *>(_result.get());
return ptr ? Result::row_size_type(PQnfields(ptr)) : 0;
}
const char *PostgreSQLResultImpl::columnName(row_size_type number) const
{
auto ptr = const_cast<PGresult *>(_result.get());
if (ptr)
{
auto N = PQfname(ptr, int(number));
assert(N);
return N;
}
throw "nullptr result"; //The program will not execute here
}
Result::size_type PostgreSQLResultImpl::affectedRows() const noexcept
{
char *str = PQcmdTuples(_result.get());
if (str == nullptr || str[0] == '\0')
return 0;
return atol(str);
}
Result::row_size_type PostgreSQLResultImpl::columnNumber(const char colName[]) const
{
auto ptr = const_cast<PGresult *>(_result.get());
if (ptr)
{
auto N = PQfnumber(ptr, colName);
if (N == -1)
throw "there is no column named ..."; // TODO throw detail exception here;
return N;
}
throw "nullptr result"; //The program will not execute here
}
const char *PostgreSQLResultImpl::getValue(size_type row, row_size_type column) const
{
return PQgetvalue(_result.get(), int(row), int(column));
}
bool PostgreSQLResultImpl::isNull(size_type row, row_size_type column) const
{
return PQgetisnull(_result.get(), int(row), int(column)) != 0;
}
Result::field_size_type PostgreSQLResultImpl::getLength(size_type row, row_size_type column) const
{
return PQgetlength(_result.get(),int(row),int(column));
}

View File

@ -0,0 +1,48 @@
/**
*
* PostgreSQLResultImpl.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.
*
*
*/
#pragma once
#include "../ResultImpl.h"
#include <libpq-fe.h>
#include <memory>
#include <string>
namespace drogon
{
namespace orm
{
class PostgreSQLResultImpl : public ResultImpl
{
public:
PostgreSQLResultImpl(const std::shared_ptr<PGresult> &r, const std::string &query) noexcept
: _result(r),
_query(query)
{
}
virtual size_type size() const noexcept override;
virtual row_size_type columns() const noexcept override;
virtual const char *columnName(row_size_type Number) const override;
virtual size_type affectedRows() const noexcept override;
virtual row_size_type columnNumber(const char colName[]) const override;
virtual const char *getValue(size_type row, row_size_type column) const override;
virtual bool isNull(size_type row, row_size_type column) const override;
virtual field_size_type getLength(size_type row, row_size_type column) const override;
private:
std::shared_ptr<PGresult> _result;
std::string _query;
};
} // namespace orm
} // namespace drogon

View File

@ -0,0 +1,3 @@
link_libraries(drogon trantor pthread dl)
add_executable(test1 test1.cc)

View File

@ -0,0 +1,97 @@
#include <drogon/orm/PgClient.h>
#include <trantor/utils/Logger.h>
#include <iostream>
#include <unistd.h>
using namespace drogon::orm;
int main()
{
drogon::orm::PgClient client("host=127.0.0.1 port=5432 dbname=trantor user=antao", 1);
LOG_DEBUG << "start!";
sleep(1);
try
{
auto r = client.execSqlSync("select * from users where user_uuid=$1;", 1);
for (auto row : r)
{
for (auto f : row)
{
std::cout << f.name() << " : " << f.as<int>() << std::endl;
}
}
}
catch (const drogon::orm::DrogonDbException &e)
{
LOG_DEBUG << "catch:" << e.base().what();
}
// client << "select count(*) from users" >> [](const drogon::orm::Result &r) {
// for (auto row : r)
// {
// for (auto f : row)
// {
// std::cout << f.name() << " : " << f.as<int>() << std::endl;
// }
// }
// } >> [](const drogon::orm::DrogonDbException &e) {
// LOG_DEBUG << "except callback:" << e.base().what();
// };
// client << "select * from users limit 5" >> [](const drogon::orm::Result &r) {
// for (auto row : r)
// {
// for (auto f : row)
// {
// std::cout << f.name() << " : " << f.as<int>() << std::endl;
// }
// }
// } >> [](const drogon::orm::DrogonDbException &e) {
// LOG_DEBUG << "except callback:" << e.base().what();
// };
// client << "select user_id,user_uuid from users where user_uuid=$1"
// << 2
// >> [](bool isNull, const std::string &id, uint64_t uuid) {
// if (!isNull)
// std::cout << "id is " << id << "(" << uuid << ")" << std::endl;
// else
// std::cout << "no more!" << std::endl;
// } >> [](const drogon::orm::DrogonDbException &e) {
// LOG_DEBUG << "except callback:" << e.base().what();
// };
// client.execSqlAsync("",
// [](const drogon::orm::Result &r) {},
// [](const drogon::orm::DrogonDbException &e) {
// LOG_DEBUG << "async nonblocking except callback:" << e.base().what();
// });
// client.execSqlAsync("",
// [](const drogon::orm::Result &r) {},
// [](const drogon::orm::DrogonDbException &e) {
// LOG_DEBUG << "async blocking except callback:" << e.base().what();
// },
// true);
auto f = client.execSqlAsync("select * from users limit 5");
try
{
auto r=f.get();
for(auto row:r)
{
std::cout<<"user_id:"<<row["user_id"].as<std::string>()<<std::endl;
}
}
catch (const drogon::orm::DrogonDbException &e)
{
LOG_DEBUG << "future exception:" << e.base().what();
}
// client << "\\d users"
// >>[](const Result &r)
// {
// std::cout<<"got a result!"<<std::endl;
// }
// >>[](const DrogonDbException &e)
// {
// std::cout<< e.base().what()<<std::endl;
// };
getchar();
}

View File

@ -6,4 +6,5 @@ add_executable(cookies_test CookiesTest.cc)
add_executable(class_name_test ClassNameTest.cc)
add_executable(sha1_test Sha1Test.cc)
add_executable(view_data_test HttpViewDataTest.cc)
add_executable(orm_result_test OrmResultTest.cc)

21
tests/OrmResultTest.cc Normal file
View File

@ -0,0 +1,21 @@
#include <drogon/orm/Result.h>
#include <drogon/orm/Row.h>
#include <drogon/orm/ResultIterator.h>
#include <drogon/orm/RowIterator.h>
#include <drogon/orm/Field.h>
#include <iostream>
int main()
{
// drogon::orm::Result r;
// for(auto row:r)
// {
// for(auto field:row)
// {
// std::cout<<field.name()<<std::endl;
// }
// }
// for(auto riter=r.rbegin();riter!=r.rend();riter++)
// {
// }
}