mirror of
https://github.com/drogonframework/drogon.git
synced 2025-09-09 00:00:40 -04:00
Start Adding orm
This commit is contained in:
parent
373a423dfe
commit
2dcb924c81
@ -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
54
COPYING
Executable 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.
|
112
orm_lib/inc/drogon/orm/DbClient.h
Normal file
112
orm_lib/inc/drogon/orm/DbClient.h
Normal 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 *> ¶meters,
|
||||
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
|
489
orm_lib/inc/drogon/orm/Exception.h
Normal file
489
orm_lib/inc/drogon/orm/Exception.h
Normal 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
|
126
orm_lib/inc/drogon/orm/Field.h
Normal file
126
orm_lib/inc/drogon/orm/Field.h
Normal 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
|
127
orm_lib/inc/drogon/orm/FunctionTraits.h
Normal file
127
orm_lib/inc/drogon/orm/FunctionTraits.h
Normal 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
|
55
orm_lib/inc/drogon/orm/Mapper.h
Normal file
55
orm_lib/inc/drogon/orm/Mapper.h
Normal 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
|
39
orm_lib/inc/drogon/orm/PgClient.h
Normal file
39
orm_lib/inc/drogon/orm/PgClient.h
Normal 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 *> ¶meters,
|
||||
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
|
105
orm_lib/inc/drogon/orm/Result.h
Normal file
105
orm_lib/inc/drogon/orm/Result.h
Normal 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
|
168
orm_lib/inc/drogon/orm/ResultIterator.h
Normal file
168
orm_lib/inc/drogon/orm/ResultIterator.h
Normal 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
|
73
orm_lib/inc/drogon/orm/Row.h
Normal file
73
orm_lib/inc/drogon/orm/Row.h
Normal 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
|
167
orm_lib/inc/drogon/orm/RowIterator.h
Normal file
167
orm_lib/inc/drogon/orm/RowIterator.h
Normal 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
|
278
orm_lib/inc/drogon/orm/SqlBinder.h
Normal file
278
orm_lib/inc/drogon/orm/SqlBinder.h
Normal 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 &¶meter)
|
||||
{
|
||||
_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
21
orm_lib/src/DbClient.cc
Normal 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
114
orm_lib/src/Exception.cc
Normal 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
50
orm_lib/src/Field.cc
Normal 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
145
orm_lib/src/Result.cc
Normal 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
35
orm_lib/src/ResultImpl.h
Normal 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
97
orm_lib/src/Row.cc
Normal 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
116
orm_lib/src/SqlBinder.cc
Normal 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();
|
||||
}
|
||||
}
|
243
orm_lib/src/postgresql_impl/DbConnection.cc
Normal file
243
orm_lib/src/postgresql_impl/DbConnection.cc
Normal 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 *> ¶meters,
|
||||
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();
|
||||
}
|
||||
}
|
74
orm_lib/src/postgresql_impl/DbConnection.h
Normal file
74
orm_lib/src/postgresql_impl/DbConnection.h
Normal 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 *> ¶meters,
|
||||
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
|
49
orm_lib/src/postgresql_impl/PgClient.cc
Normal file
49
orm_lib/src/postgresql_impl/PgClient.cc
Normal 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 *> ¶meters,
|
||||
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))
|
||||
{
|
||||
}
|
202
orm_lib/src/postgresql_impl/PgClientImpl.cc
Normal file
202
orm_lib/src/postgresql_impl/PgClientImpl.cc
Normal 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 *> ¶meters,
|
||||
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 *> ¶meters,
|
||||
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));
|
||||
}
|
||||
}
|
76
orm_lib/src/postgresql_impl/PgClientImpl.h
Normal file
76
orm_lib/src/postgresql_impl/PgClientImpl.h
Normal 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 *> ¶meters,
|
||||
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 *> ¶meters,
|
||||
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
|
68
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc
Normal file
68
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc
Normal 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));
|
||||
}
|
48
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.h
Normal file
48
orm_lib/src/postgresql_impl/PostgreSQLResultImpl.h
Normal 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
|
3
orm_lib/src/postgresql_impl/test/CMakeLists.txt
Normal file
3
orm_lib/src/postgresql_impl/test/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
link_libraries(drogon trantor pthread dl)
|
||||
|
||||
add_executable(test1 test1.cc)
|
97
orm_lib/src/postgresql_impl/test/test1.cc
Normal file
97
orm_lib/src/postgresql_impl/test/test1.cc
Normal 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();
|
||||
}
|
@ -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
21
tests/OrmResultTest.cc
Normal 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++)
|
||||
// {
|
||||
|
||||
// }
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user