mirror of
https://github.com/drogonframework/drogon.git
synced 2025-06-23 00:00:30 -04:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a22956b82b | ||
|
3c5749bbc2 | ||
|
7cd1ae8940 | ||
|
c3f9192541 | ||
|
e46e05e94a | ||
|
26e7c6913c | ||
|
8d640bafb4 | ||
|
f6b5404dbb | ||
|
46b5c9044d | ||
|
ac0d4d0f89 | ||
|
5c4057331e | ||
|
95a518e7f2 | ||
|
c03a3df106 | ||
|
d6a33f93c9 | ||
|
59cd4366c7 | ||
|
c92d146374 | ||
|
3c7c66e310 | ||
|
1fb67d68be |
10
.github/workflows/cmake.yml
vendored
10
.github/workflows/cmake.yml
vendored
@ -17,7 +17,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
windows:
|
windows:
|
||||||
name: windows/msvc - ${{ matrix.link }}
|
name: windows/msvc - ${{ matrix.link }}
|
||||||
runs-on: windows-2019
|
runs-on: windows-2022
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -44,9 +44,6 @@ jobs:
|
|||||||
- name: Create Build Environment & Configure Cmake
|
- name: Create Build Environment & Configure Cmake
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
# For unknown reasons, we fail to create file in windows ci environment.
|
|
||||||
# So examples, drogon_ctl and integration tests can not be built in windows ci.
|
|
||||||
# We should try to enable them again in the future.
|
|
||||||
run: |
|
run: |
|
||||||
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
|
[[ ${{ matrix.link }} == "SHARED" ]] && shared="ON" || shared="OFF"
|
||||||
cmake .. \
|
cmake .. \
|
||||||
@ -54,8 +51,8 @@ jobs:
|
|||||||
-DBUILD_TESTING=on \
|
-DBUILD_TESTING=on \
|
||||||
-DBUILD_SHARED_LIBS=$shared \
|
-DBUILD_SHARED_LIBS=$shared \
|
||||||
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" \
|
-DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" \
|
||||||
-DBUILD_CTL=OFF \
|
-DBUILD_CTL=ON \
|
||||||
-DBUILD_EXAMPLES=OFF \
|
-DBUILD_EXAMPLES=ON \
|
||||||
-DUSE_SPDLOG=ON \
|
-DUSE_SPDLOG=ON \
|
||||||
-DCMAKE_INSTALL_PREFIX=../install \
|
-DCMAKE_INSTALL_PREFIX=../install \
|
||||||
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW \
|
-DCMAKE_POLICY_DEFAULT_CMP0091=NEW \
|
||||||
@ -102,7 +99,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Prepare for testing
|
- name: Prepare for testing
|
||||||
run: |
|
run: |
|
||||||
brew tap homebrew/services
|
|
||||||
brew services restart postgresql@14
|
brew services restart postgresql@14
|
||||||
brew services start mariadb
|
brew services start mariadb
|
||||||
brew services start redis
|
brew services start redis
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -35,7 +35,6 @@ build/
|
|||||||
cmake-build-debug/
|
cmake-build-debug/
|
||||||
cmake-build-debug-visual-studio/
|
cmake-build-debug-visual-studio/
|
||||||
.idea/
|
.idea/
|
||||||
lib/inc/drogon/version.h
|
|
||||||
html/
|
html/
|
||||||
latex/
|
latex/
|
||||||
.vscode
|
.vscode
|
||||||
|
@ -25,7 +25,7 @@ CMAKE_DEPENDENT_OPTION(USE_SPDLOG "Allow using the spdlog logging library" OFF "
|
|||||||
|
|
||||||
set(DROGON_MAJOR_VERSION 1)
|
set(DROGON_MAJOR_VERSION 1)
|
||||||
set(DROGON_MINOR_VERSION 9)
|
set(DROGON_MINOR_VERSION 9)
|
||||||
set(DROGON_PATCH_VERSION 10)
|
set(DROGON_PATCH_VERSION 11)
|
||||||
set(DROGON_VERSION
|
set(DROGON_VERSION
|
||||||
${DROGON_MAJOR_VERSION}.${DROGON_MINOR_VERSION}.${DROGON_PATCH_VERSION})
|
${DROGON_MAJOR_VERSION}.${DROGON_MINOR_VERSION}.${DROGON_PATCH_VERSION})
|
||||||
set(DROGON_VERSION_STRING "${DROGON_VERSION}")
|
set(DROGON_VERSION_STRING "${DROGON_VERSION}")
|
||||||
@ -121,6 +121,7 @@ endif()
|
|||||||
target_include_directories(
|
target_include_directories(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc>
|
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib/inc>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/lib/inc>
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/orm_lib/inc>
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/nosql_lib/redis/inc>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/nosql_lib/redis/inc>
|
||||||
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
|
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
|
||||||
@ -342,21 +343,21 @@ set(private_headers
|
|||||||
lib/src/ConfigAdapter.h
|
lib/src/ConfigAdapter.h
|
||||||
lib/src/MultipartStreamParser.h)
|
lib/src/MultipartStreamParser.h)
|
||||||
|
|
||||||
if (NOT WIN32)
|
if (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||||
set(DROGON_SOURCES
|
set(DROGON_SOURCES
|
||||||
${DROGON_SOURCES}
|
${DROGON_SOURCES}
|
||||||
lib/src/SharedLibManager.cc)
|
lib/src/SharedLibManager.cc)
|
||||||
set(private_headers
|
set(private_headers
|
||||||
${private_headers}
|
${private_headers}
|
||||||
lib/src/SharedLibManager.h)
|
lib/src/SharedLibManager.h)
|
||||||
else (NOT WIN32)
|
elseif(WIN32)
|
||||||
set(DROGON_SOURCES
|
set(DROGON_SOURCES
|
||||||
${DROGON_SOURCES}
|
${DROGON_SOURCES}
|
||||||
third_party/mman-win32/mman.c)
|
third_party/mman-win32/mman.c)
|
||||||
set(private_headers
|
set(private_headers
|
||||||
${private_headers}
|
${private_headers}
|
||||||
third_party/mman-win32/mman.h)
|
third_party/mman-win32/mman.h)
|
||||||
endif (NOT WIN32)
|
endif()
|
||||||
|
|
||||||
if (BUILD_POSTGRESQL)
|
if (BUILD_POSTGRESQL)
|
||||||
# find postgres
|
# find postgres
|
||||||
@ -510,7 +511,7 @@ execute_process(COMMAND "git" rev-parse HEAD
|
|||||||
OUTPUT_VARIABLE GIT_SHA1
|
OUTPUT_VARIABLE GIT_SHA1
|
||||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in"
|
configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/version.h.in"
|
||||||
"${PROJECT_SOURCE_DIR}/lib/inc/drogon/version.h" @ONLY)
|
"${CMAKE_CURRENT_BINARY_DIR}/lib/inc/drogon/version.h" @ONLY)
|
||||||
|
|
||||||
if (DROGON_CXX_STANDARD EQUAL 20)
|
if (DROGON_CXX_STANDARD EQUAL 20)
|
||||||
option(USE_COROUTINE "Enable C++20 coroutine support" ON)
|
option(USE_COROUTINE "Enable C++20 coroutine support" ON)
|
||||||
@ -583,7 +584,7 @@ set(DROGON_HEADERS
|
|||||||
lib/inc/drogon/WebSocketConnection.h
|
lib/inc/drogon/WebSocketConnection.h
|
||||||
lib/inc/drogon/WebSocketController.h
|
lib/inc/drogon/WebSocketController.h
|
||||||
lib/inc/drogon/drogon.h
|
lib/inc/drogon/drogon.h
|
||||||
lib/inc/drogon/version.h
|
${CMAKE_CURRENT_BINARY_DIR}/lib/inc/drogon/version.h
|
||||||
lib/inc/drogon/drogon_callbacks.h
|
lib/inc/drogon/drogon_callbacks.h
|
||||||
lib/inc/drogon/PubSubService.h
|
lib/inc/drogon/PubSubService.h
|
||||||
lib/inc/drogon/drogon_test.h
|
lib/inc/drogon/drogon_test.h
|
||||||
|
41
ChangeLog.md
41
ChangeLog.md
@ -4,6 +4,43 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.9.11] - 2025-06-20
|
||||||
|
|
||||||
|
### API changes list
|
||||||
|
|
||||||
|
- Add a new overload for execSqlCoro.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Do not write to source directory during build.
|
||||||
|
|
||||||
|
- Improve Postgres connection stability.
|
||||||
|
|
||||||
|
- Add handleFatalError in handleClosed.
|
||||||
|
|
||||||
|
- Add -o|--output option to drogon_ctl create models.
|
||||||
|
|
||||||
|
- Add qrcode for WeChat official account to the README file.
|
||||||
|
|
||||||
|
- Support for iOS compiling.
|
||||||
|
|
||||||
|
- Add cors example to demonstrate cross-origin support in drogon.
|
||||||
|
|
||||||
|
- Add support for continuation frame in WebSocketMessageParser.
|
||||||
|
|
||||||
|
- Add RawParameter API to pass raw SQL parameters.
|
||||||
|
|
||||||
|
- Upgrade Windows image and re-enable tests on Windows.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix a bug in isAutoCreationClass<T>.
|
||||||
|
|
||||||
|
- Fix CI on MacOS.
|
||||||
|
|
||||||
|
- Fix issue with precision loss of double-type parameters in ORM inputs.
|
||||||
|
|
||||||
|
|
||||||
## [1.9.10] - 2025-02-20
|
## [1.9.10] - 2025-02-20
|
||||||
|
|
||||||
### API changes list
|
### API changes list
|
||||||
@ -1805,7 +1842,9 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [1.0.0-beta1] - 2019-06-11
|
## [1.0.0-beta1] - 2019-06-11
|
||||||
|
|
||||||
[Unreleased]: https://github.com/an-tao/drogon/compare/v1.9.10...HEAD
|
[Unreleased]: https://github.com/an-tao/drogon/compare/v1.9.11...HEAD
|
||||||
|
|
||||||
|
[1.9.11]: https://github.com/an-tao/drogon/compare/v1.9.10...v1.9.11
|
||||||
|
|
||||||
[1.9.10]: https://github.com/an-tao/drogon/compare/v1.9.9...v1.9.10
|
[1.9.10]: https://github.com/an-tao/drogon/compare/v1.9.9...v1.9.10
|
||||||
|
|
||||||
|
@ -197,3 +197,9 @@ class User : public drogon::HttpController<User>
|
|||||||
## QQ交流群:1137909452
|
## QQ交流群:1137909452
|
||||||
|
|
||||||
欢迎交流探讨。
|
欢迎交流探讨。
|
||||||
|
|
||||||
|
## 微信公众号:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
会不定期推送一些Drogon的使用技巧和更新信息,欢迎关注。
|
@ -42,7 +42,8 @@ std::string create::detail()
|
|||||||
"create a plugin named class_name\n\n"
|
"create a plugin named class_name\n\n"
|
||||||
"drogon_ctl create project <project_name> //"
|
"drogon_ctl create project <project_name> //"
|
||||||
"create a project named project_name\n\n"
|
"create a project named project_name\n\n"
|
||||||
"drogon_ctl create model <model_path> [--table=<table_name>] [-f]//"
|
"drogon_ctl create model <model_path> [-o <output path>] "
|
||||||
|
"[--table=<table_name>] [-f]//"
|
||||||
"create model classes in model_path\n";
|
"create model classes in model_path\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -826,6 +826,7 @@ void create_model::createModel(const std::string &path,
|
|||||||
auto restfulApiConfig = config["restful_api_controllers"];
|
auto restfulApiConfig = config["restful_api_controllers"];
|
||||||
auto relationships = getRelationships(config["relationships"]);
|
auto relationships = getRelationships(config["relationships"]);
|
||||||
auto convertMethods = getConvertMethods(config["convert"]);
|
auto convertMethods = getConvertMethods(config["convert"]);
|
||||||
|
drogon::utils::createPath(path);
|
||||||
if (dbType == "postgresql")
|
if (dbType == "postgresql")
|
||||||
{
|
{
|
||||||
#if USE_POSTGRESQL
|
#if USE_POSTGRESQL
|
||||||
@ -1173,7 +1174,9 @@ void create_model::createModel(const std::string &path,
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
infile >> configJsonRoot;
|
infile >> configJsonRoot;
|
||||||
createModel(path, configJsonRoot, singleModelName);
|
createModel(outputPath_.empty() ? path : outputPath_,
|
||||||
|
configJsonRoot,
|
||||||
|
singleModelName);
|
||||||
}
|
}
|
||||||
catch (const std::exception &exception)
|
catch (const std::exception &exception)
|
||||||
{
|
{
|
||||||
@ -1211,6 +1214,22 @@ void create_model::handleCommand(std::vector<std::string> ¶meters)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto iter = parameters.begin(); iter != parameters.end();)
|
||||||
|
{
|
||||||
|
auto &file = *iter;
|
||||||
|
if (file == "-o" || file == "--output")
|
||||||
|
{
|
||||||
|
iter = parameters.erase(iter);
|
||||||
|
if (iter != parameters.end())
|
||||||
|
{
|
||||||
|
outputPath_ = *iter;
|
||||||
|
iter = parameters.erase(iter);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
|
||||||
for (auto const &path : parameters)
|
for (auto const &path : parameters)
|
||||||
{
|
{
|
||||||
createModel(path, singleModelName);
|
createModel(path, singleModelName);
|
||||||
|
@ -429,5 +429,6 @@ class create_model : public DrObject<create_model>, public CommandHandler
|
|||||||
const Json::Value &restfulApiConfig);
|
const Json::Value &restfulApiConfig);
|
||||||
std::string dbname_;
|
std::string dbname_;
|
||||||
bool forceOverwrite_{false};
|
bool forceOverwrite_{false};
|
||||||
|
std::string outputPath_;
|
||||||
};
|
};
|
||||||
} // namespace drogon_ctl
|
} // namespace drogon_ctl
|
||||||
|
@ -34,6 +34,8 @@ add_executable(redis_chat redis_chat/main.cc
|
|||||||
add_executable(async_stream async_stream/main.cc
|
add_executable(async_stream async_stream/main.cc
|
||||||
async_stream/RequestStreamExampleCtrl.cc)
|
async_stream/RequestStreamExampleCtrl.cc)
|
||||||
|
|
||||||
|
add_executable(cors cors/main.cc)
|
||||||
|
|
||||||
set(example_targets
|
set(example_targets
|
||||||
benchmark
|
benchmark
|
||||||
client
|
client
|
||||||
@ -45,7 +47,8 @@ set(example_targets
|
|||||||
jsonstore
|
jsonstore
|
||||||
redis_simple
|
redis_simple
|
||||||
redis_chat
|
redis_chat
|
||||||
async_stream)
|
async_stream
|
||||||
|
cors)
|
||||||
|
|
||||||
# Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear
|
# Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear
|
||||||
# when the templated functions are instantiated at their point of use.
|
# when the templated functions are instantiated at their point of use.
|
||||||
|
@ -16,6 +16,7 @@ proxy with a simple round robin
|
|||||||
11. [redis_cache](https://github.com/drogonframework/drogon/tree/master/examples/redis_cache) - An example for using coroutines of Redis clients
|
11. [redis_cache](https://github.com/drogonframework/drogon/tree/master/examples/redis_cache) - An example for using coroutines of Redis clients
|
||||||
12. [redis_chat](https://github.com/drogonframework/drogon/tree/master/examples/redis_chat) - A chatroom server built with websocket and Redis pub/sub service
|
12. [redis_chat](https://github.com/drogonframework/drogon/tree/master/examples/redis_chat) - A chatroom server built with websocket and Redis pub/sub service
|
||||||
13. [prometheus_example](https://github.com/drogonframework/drogon/tree/master/examples/prometheus_example) - An example of how to use the Prometheus exporter in Drogon
|
13. [prometheus_example](https://github.com/drogonframework/drogon/tree/master/examples/prometheus_example) - An example of how to use the Prometheus exporter in Drogon
|
||||||
|
14. [cors](https://github.com/drogonframework/drogon/tree/master/examples/cors) - An example demonstrating how to implement CORS (Cross-Origin Resource Sharing) support in Drogon
|
||||||
|
|
||||||
### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite
|
### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite
|
||||||
|
|
||||||
|
153
examples/cors/main.cc
Normal file
153
examples/cors/main.cc
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include <drogon/HttpAppFramework.h>
|
||||||
|
#include <drogon/HttpResponse.h>
|
||||||
|
#include <drogon/drogon.h>
|
||||||
|
#include "trantor/utils/Logger.h"
|
||||||
|
|
||||||
|
using namespace drogon;
|
||||||
|
|
||||||
|
/// Configure Cross-Origin Resource Sharing (CORS) support.
|
||||||
|
///
|
||||||
|
/// This function registers both synchronous pre-processing advice for handling
|
||||||
|
/// OPTIONS preflight requests and post-handling advice to inject CORS headers
|
||||||
|
/// into all responses dynamically based on the incoming request headers.
|
||||||
|
void setupCors()
|
||||||
|
{
|
||||||
|
// Register sync advice to handle CORS preflight (OPTIONS) requests
|
||||||
|
drogon::app().registerSyncAdvice([](const drogon::HttpRequestPtr &req)
|
||||||
|
-> drogon::HttpResponsePtr {
|
||||||
|
if (req->method() == drogon::HttpMethod::Options)
|
||||||
|
{
|
||||||
|
auto resp = drogon::HttpResponse::newHttpResponse();
|
||||||
|
|
||||||
|
// Set Access-Control-Allow-Origin header based on the Origin
|
||||||
|
// request header
|
||||||
|
const auto &origin = req->getHeader("Origin");
|
||||||
|
if (!origin.empty())
|
||||||
|
{
|
||||||
|
resp->addHeader("Access-Control-Allow-Origin", origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Access-Control-Allow-Methods based on the requested method
|
||||||
|
const auto &requestMethod =
|
||||||
|
req->getHeader("Access-Control-Request-Method");
|
||||||
|
if (!requestMethod.empty())
|
||||||
|
{
|
||||||
|
resp->addHeader("Access-Control-Allow-Methods", requestMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow credentials to be included in cross-origin requests
|
||||||
|
resp->addHeader("Access-Control-Allow-Credentials", "true");
|
||||||
|
|
||||||
|
// Set allowed headers from the Access-Control-Request-Headers
|
||||||
|
// header
|
||||||
|
const auto &requestHeaders =
|
||||||
|
req->getHeader("Access-Control-Request-Headers");
|
||||||
|
if (!requestHeaders.empty())
|
||||||
|
{
|
||||||
|
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(resp);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register post-handling advice to add CORS headers to all responses
|
||||||
|
drogon::app().registerPostHandlingAdvice(
|
||||||
|
[](const drogon::HttpRequestPtr &req,
|
||||||
|
const drogon::HttpResponsePtr &resp) -> void {
|
||||||
|
// Set Access-Control-Allow-Origin based on the Origin request
|
||||||
|
// header
|
||||||
|
const auto &origin = req->getHeader("Origin");
|
||||||
|
if (!origin.empty())
|
||||||
|
{
|
||||||
|
resp->addHeader("Access-Control-Allow-Origin", origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reflect the requested Access-Control-Request-Method back in the
|
||||||
|
// response
|
||||||
|
const auto &requestMethod =
|
||||||
|
req->getHeader("Access-Control-Request-Method");
|
||||||
|
if (!requestMethod.empty())
|
||||||
|
{
|
||||||
|
resp->addHeader("Access-Control-Allow-Methods", requestMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow credentials to be included in cross-origin requests
|
||||||
|
resp->addHeader("Access-Control-Allow-Credentials", "true");
|
||||||
|
|
||||||
|
// Reflect the requested Access-Control-Request-Headers back
|
||||||
|
const auto &requestHeaders =
|
||||||
|
req->getHeader("Access-Control-Request-Headers");
|
||||||
|
if (!requestHeaders.empty())
|
||||||
|
{
|
||||||
|
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main function to start the Drogon application with CORS-enabled routes.
|
||||||
|
* This example includes:
|
||||||
|
* - A simple GET endpoint `/hello` that returns a greeting message.
|
||||||
|
* - A POST endpoint `/echo` that echoes back the request body.
|
||||||
|
* You can test with curl to test the CORS support:
|
||||||
|
*
|
||||||
|
```
|
||||||
|
curl -i -X OPTIONS http://localhost:8000/echo \
|
||||||
|
-H "Origin: http://localhost:3000" \
|
||||||
|
-H "Access-Control-Request-Method: POST" \
|
||||||
|
-H "Access-Control-Request-Headers: Content-Type"
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```
|
||||||
|
curl -i -X POST http://localhost:8000/echo \
|
||||||
|
-H "Origin: http://localhost:3000" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"key":"value"}'
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// Listen on port 8000 for all interfaces
|
||||||
|
app().addListener("0.0.0.0", 8000);
|
||||||
|
|
||||||
|
// Setup CORS support
|
||||||
|
setupCors();
|
||||||
|
|
||||||
|
// Register /hello route for GET and OPTIONS methods
|
||||||
|
app().registerHandler(
|
||||||
|
"/hello",
|
||||||
|
[](const HttpRequestPtr &req,
|
||||||
|
std::function<void(const HttpResponsePtr &)> &&callback) {
|
||||||
|
auto resp = HttpResponse::newHttpResponse();
|
||||||
|
resp->setBody("Hello from Drogon!");
|
||||||
|
|
||||||
|
// Log client IP address
|
||||||
|
LOG_INFO << "Request to /hello from " << req->getPeerAddr().toIp();
|
||||||
|
|
||||||
|
callback(resp);
|
||||||
|
},
|
||||||
|
{Get, Options});
|
||||||
|
|
||||||
|
// Register /echo route for POST and OPTIONS methods
|
||||||
|
app().registerHandler(
|
||||||
|
"/echo",
|
||||||
|
[](const HttpRequestPtr &req,
|
||||||
|
std::function<void(const HttpResponsePtr &)> &&callback) {
|
||||||
|
auto resp = HttpResponse::newHttpResponse();
|
||||||
|
resp->setBody(std::string("Echo: ").append(req->getBody()));
|
||||||
|
|
||||||
|
// Log client IP and request body
|
||||||
|
LOG_INFO << "Request to /echo from " << req->getPeerAddr().toIp();
|
||||||
|
LOG_INFO << "Echo content: " << req->getBody();
|
||||||
|
|
||||||
|
callback(resp);
|
||||||
|
},
|
||||||
|
{Post, Options});
|
||||||
|
|
||||||
|
// Start the application main loop
|
||||||
|
app().run();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -61,9 +61,9 @@ template <typename T>
|
|||||||
struct isAutoCreationClass
|
struct isAutoCreationClass
|
||||||
{
|
{
|
||||||
template <class C>
|
template <class C>
|
||||||
static constexpr auto check(C *)
|
static constexpr auto check(C *) -> std::enable_if_t<
|
||||||
-> std::enable_if_t<std::is_same_v<decltype(C::isAutoCreation), bool>,
|
std::is_same_v<decltype(C::isAutoCreation), const bool>,
|
||||||
bool>
|
bool>
|
||||||
{
|
{
|
||||||
return C::isAutoCreation;
|
return C::isAutoCreation;
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,16 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && defined(__MACH__) && \
|
||||||
|
(defined(__ENVIRONMENT_IPHONE_OS__) || \
|
||||||
|
defined(__IPHONE_OS_VERSION_MIN_REQUIRED))
|
||||||
|
// iOS
|
||||||
|
#define TARGET_OS_IOS 1
|
||||||
|
#else
|
||||||
|
// not iOS
|
||||||
|
#define TARGET_OS_IOS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace drogon
|
namespace drogon
|
||||||
{
|
{
|
||||||
// the drogon banner
|
// the drogon banner
|
||||||
@ -997,7 +1007,7 @@ class DROGON_EXPORT HttpAppFramework : public trantor::NonCopyable
|
|||||||
virtual HttpAppFramework &setFileTypes(
|
virtual HttpAppFramework &setFileTypes(
|
||||||
const std::vector<std::string> &types) = 0;
|
const std::vector<std::string> &types) = 0;
|
||||||
|
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !TARGET_OS_IOS
|
||||||
/// Enable supporting for dynamic views loading.
|
/// Enable supporting for dynamic views loading.
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -383,7 +383,7 @@ static void loadApp(const Json::Value &app)
|
|||||||
{
|
{
|
||||||
drogon::app().setMaxConnectionNumPerIP(maxConnsPerIP);
|
drogon::app().setMaxConnectionNumPerIP(maxConnsPerIP);
|
||||||
}
|
}
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !TARGET_OS_IOS
|
||||||
// dynamic views
|
// dynamic views
|
||||||
auto enableDynamicViews = app.get("load_dynamic_views", false).asBool();
|
auto enableDynamicViews = app.get("load_dynamic_views", false).asBool();
|
||||||
if (enableDynamicViews)
|
if (enableDynamicViews)
|
||||||
|
@ -184,7 +184,7 @@ static void TERMFunction(int sig)
|
|||||||
HttpAppFrameworkImpl::~HttpAppFrameworkImpl() noexcept
|
HttpAppFrameworkImpl::~HttpAppFrameworkImpl() noexcept
|
||||||
{
|
{
|
||||||
// Destroy the following objects before the loop destruction
|
// Destroy the following objects before the loop destruction
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !TARGET_OS_IOS
|
||||||
sharedLibManagerPtr_.reset();
|
sharedLibManagerPtr_.reset();
|
||||||
#endif
|
#endif
|
||||||
sessionManagerPtr_.reset();
|
sessionManagerPtr_.reset();
|
||||||
@ -236,7 +236,7 @@ const std::string &HttpAppFrameworkImpl::getImplicitPage() const
|
|||||||
{
|
{
|
||||||
return StaticFileRouter::instance().getImplicitPage();
|
return StaticFileRouter::instance().getImplicitPage();
|
||||||
}
|
}
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !TARGET_OS_IOS
|
||||||
HttpAppFramework &HttpAppFrameworkImpl::enableDynamicViewsLoading(
|
HttpAppFramework &HttpAppFrameworkImpl::enableDynamicViewsLoading(
|
||||||
const std::vector<std::string> &libPaths,
|
const std::vector<std::string> &libPaths,
|
||||||
const std::string &outputPath)
|
const std::string &outputPath)
|
||||||
@ -599,7 +599,7 @@ void HttpAppFrameworkImpl::run()
|
|||||||
LOG_INFO << "Start child process";
|
LOG_INFO << "Start child process";
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !TARGET_OS_IOS
|
||||||
if (!libFilePaths_.empty())
|
if (!libFilePaths_.empty())
|
||||||
{
|
{
|
||||||
sharedLibManagerPtr_ =
|
sharedLibManagerPtr_ =
|
||||||
|
@ -267,7 +267,7 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
|
|||||||
HttpAppFramework &setUploadPath(const std::string &uploadPath) override;
|
HttpAppFramework &setUploadPath(const std::string &uploadPath) override;
|
||||||
HttpAppFramework &setFileTypes(
|
HttpAppFramework &setFileTypes(
|
||||||
const std::vector<std::string> &types) override;
|
const std::vector<std::string> &types) override;
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !TARGET_OS_IOS
|
||||||
HttpAppFramework &enableDynamicViewsLoading(
|
HttpAppFramework &enableDynamicViewsLoading(
|
||||||
const std::vector<std::string> &libPaths,
|
const std::vector<std::string> &libPaths,
|
||||||
const std::string &outputPath) override;
|
const std::string &outputPath) override;
|
||||||
@ -709,7 +709,7 @@ class HttpAppFrameworkImpl final : public HttpAppFramework
|
|||||||
size_t threadNum_{1};
|
size_t threadNum_{1};
|
||||||
std::unique_ptr<trantor::EventLoopThreadPool> ioLoopThreadPool_;
|
std::unique_ptr<trantor::EventLoopThreadPool> ioLoopThreadPool_;
|
||||||
|
|
||||||
#ifndef _WIN32
|
#if !defined(_WIN32) && !TARGET_OS_IOS
|
||||||
std::vector<std::string> libFilePaths_;
|
std::vector<std::string> libFilePaths_;
|
||||||
std::string libFileOutputPath_;
|
std::string libFileOutputPath_;
|
||||||
std::unique_ptr<SharedLibManager> sharedLibManagerPtr_;
|
std::unique_ptr<SharedLibManager> sharedLibManagerPtr_;
|
||||||
|
@ -44,7 +44,7 @@ void StaticFileRouter::init(const std::vector<trantor::EventLoop *> &ioLoops)
|
|||||||
size_t i) {
|
size_t i) {
|
||||||
assert(i == ioLoops[i]->index());
|
assert(i == ioLoops[i]->index());
|
||||||
mapPtr = std::make_unique<CacheMap<std::string, char>>(ioLoops[i],
|
mapPtr = std::make_unique<CacheMap<std::string, char>>(ioLoops[i],
|
||||||
1.0,
|
1.0f,
|
||||||
4,
|
4,
|
||||||
50);
|
50);
|
||||||
});
|
});
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <json/value.h>
|
#include <json/value.h>
|
||||||
#include <json/writer.h>
|
#include <json/writer.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace drogon;
|
using namespace drogon;
|
||||||
|
|
||||||
@ -268,14 +269,14 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
{
|
{
|
||||||
// According to the rfc6455
|
// According to the rfc6455
|
||||||
gotAll_ = false;
|
gotAll_ = false;
|
||||||
if (buffer->readableBytes() >= 2)
|
while (buffer->readableBytes() >= 2)
|
||||||
{
|
{
|
||||||
unsigned char opcode = (*buffer)[0] & 0x0f;
|
unsigned char opcode = (*buffer)[0] & 0x0f;
|
||||||
bool isControlFrame = false;
|
bool isControlFrame = false;
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
// continuation frame
|
LOG_TRACE << "continuation frame";
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
type_ = WebSocketMessageType::Text;
|
type_ = WebSocketMessageType::Text;
|
||||||
@ -327,8 +328,13 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
{
|
{
|
||||||
indexFirstMask = 10;
|
indexFirstMask = 10;
|
||||||
}
|
}
|
||||||
if (indexFirstMask > 2 && buffer->readableBytes() >= indexFirstMask)
|
if (indexFirstMask > 2)
|
||||||
{
|
{
|
||||||
|
if (buffer->readableBytes() < indexFirstMask)
|
||||||
|
{
|
||||||
|
// Not enough data yet, wait for more.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (isControlFrame)
|
if (isControlFrame)
|
||||||
{
|
{
|
||||||
// rfc6455-5.5
|
// rfc6455-5.5
|
||||||
@ -344,14 +350,17 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
}
|
}
|
||||||
else if (indexFirstMask == 10)
|
else if (indexFirstMask == 10)
|
||||||
{
|
{
|
||||||
length = (unsigned char)(*buffer)[2];
|
length = 0;
|
||||||
length = (length << 8) + (unsigned char)(*buffer)[3];
|
for (int i = 2; i <= 9; ++i)
|
||||||
length = (length << 8) + (unsigned char)(*buffer)[4];
|
{
|
||||||
length = (length << 8) + (unsigned char)(*buffer)[5];
|
if (length > ((std::numeric_limits<size_t>::max)() >> 8))
|
||||||
length = (length << 8) + (unsigned char)(*buffer)[6];
|
{
|
||||||
length = (length << 8) + (unsigned char)(*buffer)[7];
|
LOG_ERROR
|
||||||
length = (length << 8) + (unsigned char)(*buffer)[8];
|
<< "Payload length too large to handle safely";
|
||||||
length = (length << 8) + (unsigned char)(*buffer)[9];
|
return false;
|
||||||
|
}
|
||||||
|
length = (length << 8) + (unsigned char)(*buffer)[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -380,9 +389,16 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
{
|
{
|
||||||
message_[oldLen + i] = (rawData[i] ^ masks[i % 4]);
|
message_[oldLen + i] = (rawData[i] ^ masks[i % 4]);
|
||||||
}
|
}
|
||||||
if (isFin)
|
|
||||||
gotAll_ = true;
|
|
||||||
buffer->retrieve(indexFirstMask + 4 + length);
|
buffer->retrieve(indexFirstMask + 4 + length);
|
||||||
|
if (isFin)
|
||||||
|
{
|
||||||
|
gotAll_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not enough data yet, wait for more.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -392,9 +408,16 @@ bool WebSocketMessageParser::parse(trantor::MsgBuffer *buffer)
|
|||||||
{
|
{
|
||||||
auto rawData = buffer->peek() + indexFirstMask;
|
auto rawData = buffer->peek() + indexFirstMask;
|
||||||
message_.append(rawData, length);
|
message_.append(rawData, length);
|
||||||
if (isFin)
|
|
||||||
gotAll_ = true;
|
|
||||||
buffer->retrieve(indexFirstMask + length);
|
buffer->retrieve(indexFirstMask + length);
|
||||||
|
if (isFin)
|
||||||
|
{
|
||||||
|
gotAll_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not enough data yet, wait for more.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <drogon/DrObject.h>
|
#include <drogon/DrObject.h>
|
||||||
#include <drogon/drogon_test.h>
|
#include <drogon/drogon_test.h>
|
||||||
|
#include <drogon/HttpController.h>
|
||||||
|
|
||||||
using namespace drogon;
|
using namespace drogon;
|
||||||
|
|
||||||
@ -41,3 +42,45 @@ DROGON_TEST(DrObjectNamespaceTest)
|
|||||||
CHECK(objPtr2.get() != nullptr);
|
CHECK(objPtr2.get() != nullptr);
|
||||||
CHECK(objPtr == objPtr2);
|
CHECK(objPtr == objPtr2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TestC : public DrObject<TestC>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr bool isAutoCreation = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestD : public DrObject<TestD>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr bool isAutoCreation = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestE : public DrObject<TestE>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr double isAutoCreation = 3.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CtrlA : public HttpController<CtrlA>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
METHOD_LIST_BEGIN
|
||||||
|
METHOD_LIST_END
|
||||||
|
};
|
||||||
|
|
||||||
|
class CtrlB : public HttpController<CtrlB, false>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
METHOD_LIST_BEGIN
|
||||||
|
METHOD_LIST_END
|
||||||
|
};
|
||||||
|
|
||||||
|
DROGON_TEST(IsAutoCreationClassTest)
|
||||||
|
{
|
||||||
|
STATIC_REQUIRE(isAutoCreationClass<TestA>::value == false);
|
||||||
|
STATIC_REQUIRE(isAutoCreationClass<TestC>::value == true);
|
||||||
|
STATIC_REQUIRE(isAutoCreationClass<TestD>::value == false);
|
||||||
|
STATIC_REQUIRE(isAutoCreationClass<TestE>::value == false);
|
||||||
|
STATIC_REQUIRE(isAutoCreationClass<CtrlA>::value == true);
|
||||||
|
STATIC_REQUIRE(isAutoCreationClass<CtrlB>::value == false);
|
||||||
|
}
|
||||||
|
@ -214,6 +214,28 @@ class DROGON_EXPORT DbClient : public trantor::NonCopyable
|
|||||||
(binder << std::forward<Arguments>(args), 0)...};
|
(binder << std::forward<Arguments>(args), 0)...};
|
||||||
return internal::SqlAwaiter(std::move(binder));
|
return internal::SqlAwaiter(std::move(binder));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Execute a SQL query asynchronously using coroutine support.
|
||||||
|
* This overload accepts a vector of arguments to bind to the query.
|
||||||
|
* @tparam T The type of the elements in the vector.
|
||||||
|
* @param sql The SQL query string to execute.
|
||||||
|
* @param args A vector of arguments to bind to the query.
|
||||||
|
* @return A SqlAwaiter object that can be co_awaited to retrieve the query
|
||||||
|
* result.
|
||||||
|
* @note This method is only available when coroutine support is enabled.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
internal::SqlAwaiter execSqlCoro(const std::string &sql,
|
||||||
|
const std::vector<T> &args) noexcept
|
||||||
|
{
|
||||||
|
auto binder = *this << sql;
|
||||||
|
for (const auto &arg : args)
|
||||||
|
{
|
||||||
|
binder << arg;
|
||||||
|
}
|
||||||
|
return internal::SqlAwaiter(std::move(binder));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Streaming-like method for sql execution. For more information, see the
|
/// Streaming-like method for sql execution. For more information, see the
|
||||||
|
@ -116,6 +116,14 @@ enum class Mode
|
|||||||
Blocking
|
Blocking
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RawParameter
|
||||||
|
{
|
||||||
|
std::shared_ptr<void> obj;
|
||||||
|
const char *parameter;
|
||||||
|
int length;
|
||||||
|
int format;
|
||||||
|
};
|
||||||
|
|
||||||
namespace internal
|
namespace internal
|
||||||
{
|
{
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -434,6 +442,15 @@ class DROGON_EXPORT SqlBinder : public trantor::NonCopyable
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self &operator<<(const RawParameter &);
|
||||||
|
|
||||||
|
self &operator<<(RawParameter ¶m)
|
||||||
|
{
|
||||||
|
return operator<<((const RawParameter &)param);
|
||||||
|
}
|
||||||
|
|
||||||
|
self &operator<<(RawParameter &&);
|
||||||
|
|
||||||
// template <>
|
// template <>
|
||||||
self &operator<<(const char str[])
|
self &operator<<(const char str[])
|
||||||
{
|
{
|
||||||
|
@ -429,6 +429,8 @@ DbConnectionPtr DbClientImpl::newConnection(trantor::EventLoop *loop)
|
|||||||
}
|
}
|
||||||
// Reconnect after 1 second
|
// Reconnect after 1 second
|
||||||
auto loop = closeConnPtr->loop();
|
auto loop = closeConnPtr->loop();
|
||||||
|
// closeConnPtr may be not valid. Close the connection file descriptor.
|
||||||
|
closeConnPtr->disconnect();
|
||||||
loop->runAfter(1, [weakPtr, loop, closeConnPtr] {
|
loop->runAfter(1, [weakPtr, loop, closeConnPtr] {
|
||||||
auto thisPtr = weakPtr.lock();
|
auto thisPtr = weakPtr.lock();
|
||||||
if (!thisPtr)
|
if (!thisPtr)
|
||||||
|
@ -118,7 +118,7 @@ class DbConnection : public trantor::NonCopyable
|
|||||||
|
|
||||||
virtual ~DbConnection()
|
virtual ~DbConnection()
|
||||||
{
|
{
|
||||||
LOG_TRACE << "Destruct DbConn" << this;
|
LOG_TRACE << "Destruct DbConn " << this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectStatus status() const
|
ConnectStatus status() const
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
#include <drogon/utils/Utilities.h>
|
#include <drogon/utils/Utilities.h>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#if defined(__cpp_lib_format)
|
||||||
|
#include <format>
|
||||||
|
#endif
|
||||||
#if USE_MYSQL
|
#if USE_MYSQL
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
#endif
|
#endif
|
||||||
@ -134,6 +137,26 @@ SqlBinder::~SqlBinder()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SqlBinder &SqlBinder::operator<<(const RawParameter ¶m)
|
||||||
|
{
|
||||||
|
objs_.push_back(param.obj);
|
||||||
|
parameters_.push_back(param.parameter);
|
||||||
|
lengths_.push_back(param.length);
|
||||||
|
formats_.push_back(param.format);
|
||||||
|
++parametersNumber_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SqlBinder &SqlBinder::operator<<(RawParameter &¶m)
|
||||||
|
{
|
||||||
|
objs_.push_back(std::move(param.obj));
|
||||||
|
parameters_.push_back(std::move(param.parameter));
|
||||||
|
lengths_.push_back(std::move(param.length));
|
||||||
|
formats_.push_back(std::move(param.format));
|
||||||
|
++parametersNumber_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
SqlBinder &SqlBinder::operator<<(const std::string_view &str)
|
SqlBinder &SqlBinder::operator<<(const std::string_view &str)
|
||||||
{
|
{
|
||||||
auto obj = std::make_shared<std::string>(str.data(), str.length());
|
auto obj = std::make_shared<std::string>(str.data(), str.length());
|
||||||
@ -259,7 +282,14 @@ SqlBinder &SqlBinder::operator<<(double f)
|
|||||||
parameters_.push_back((char *)(obj.get()));
|
parameters_.push_back((char *)(obj.get()));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
return operator<<(std::to_string(f));
|
|
||||||
|
#if defined(__cpp_lib_format)
|
||||||
|
return operator<<(std::format("{:.17g}", f));
|
||||||
|
#else
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::setprecision(17) << f;
|
||||||
|
return operator<<(ss.str());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SqlBinder &SqlBinder::operator<<(std::nullptr_t)
|
SqlBinder &SqlBinder::operator<<(std::nullptr_t)
|
||||||
|
@ -87,11 +87,14 @@ PgConnection::PgConnection(trantor::EventLoop *loop,
|
|||||||
[](PGconn *conn) { PQfinish(conn); })),
|
[](PGconn *conn) { PQfinish(conn); })),
|
||||||
channel_(loop, PQsocket(connectionPtr_.get()))
|
channel_(loop, PQsocket(connectionPtr_.get()))
|
||||||
{
|
{
|
||||||
|
if (channel_.fd() < 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR << "Failed to create Postgres connection";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PgConnection::init()
|
void PgConnection::init()
|
||||||
{
|
{
|
||||||
PQsetnonblocking(connectionPtr_.get(), 1);
|
|
||||||
if (channel_.fd() < 0)
|
if (channel_.fd() < 0)
|
||||||
{
|
{
|
||||||
LOG_ERROR << "Connection with Postgres could not be established";
|
LOG_ERROR << "Connection with Postgres could not be established";
|
||||||
@ -103,6 +106,8 @@ void PgConnection::init()
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PQsetnonblocking(connectionPtr_.get(), 1);
|
||||||
channel_.setReadCallback([this]() {
|
channel_.setReadCallback([this]() {
|
||||||
if (status_ == ConnectStatus::Bad)
|
if (status_ == ConnectStatus::Bad)
|
||||||
{
|
{
|
||||||
@ -165,8 +170,11 @@ void PgConnection::disconnect()
|
|||||||
auto thisPtr = shared_from_this();
|
auto thisPtr = shared_from_this();
|
||||||
loop_->runInLoop([thisPtr, &pro]() {
|
loop_->runInLoop([thisPtr, &pro]() {
|
||||||
thisPtr->status_ = ConnectStatus::Bad;
|
thisPtr->status_ = ConnectStatus::Bad;
|
||||||
thisPtr->channel_.disableAll();
|
if (thisPtr->channel_.fd() >= 0)
|
||||||
thisPtr->channel_.remove();
|
{
|
||||||
|
thisPtr->channel_.disableAll();
|
||||||
|
thisPtr->channel_.remove();
|
||||||
|
}
|
||||||
thisPtr->connectionPtr_.reset();
|
thisPtr->connectionPtr_.reset();
|
||||||
pro.set_value(1);
|
pro.set_value(1);
|
||||||
});
|
});
|
||||||
@ -522,11 +530,17 @@ void PgConnection::handleFatalError(bool clearAll, bool isAbortPipeline)
|
|||||||
{
|
{
|
||||||
for (auto &cmd : batchCommandsForWaitingResults_)
|
for (auto &cmd : batchCommandsForWaitingResults_)
|
||||||
{
|
{
|
||||||
cmd->exceptionCallback_(exceptPtr);
|
if (cmd->exceptionCallback_)
|
||||||
|
{
|
||||||
|
cmd->exceptionCallback_(exceptPtr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (auto &cmd : batchSqlCommands_)
|
for (auto &cmd : batchSqlCommands_)
|
||||||
{
|
{
|
||||||
cmd->exceptionCallback_(exceptPtr);
|
if (cmd->exceptionCallback_)
|
||||||
|
{
|
||||||
|
cmd->exceptionCallback_(exceptPtr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
batchCommandsForWaitingResults_.clear();
|
batchCommandsForWaitingResults_.clear();
|
||||||
batchSqlCommands_.clear();
|
batchSqlCommands_.clear();
|
||||||
@ -536,13 +550,19 @@ void PgConnection::handleFatalError(bool clearAll, bool isAbortPipeline)
|
|||||||
if (!batchSqlCommands_.empty() &&
|
if (!batchSqlCommands_.empty() &&
|
||||||
!batchSqlCommands_.front()->preparingStatement_.empty())
|
!batchSqlCommands_.front()->preparingStatement_.empty())
|
||||||
{
|
{
|
||||||
batchSqlCommands_.front()->exceptionCallback_(exceptPtr);
|
if (batchSqlCommands_.front()->exceptionCallback_)
|
||||||
|
{
|
||||||
|
batchSqlCommands_.front()->exceptionCallback_(exceptPtr);
|
||||||
|
}
|
||||||
batchSqlCommands_.pop_front();
|
batchSqlCommands_.pop_front();
|
||||||
}
|
}
|
||||||
else if (!batchCommandsForWaitingResults_.empty())
|
else if (!batchCommandsForWaitingResults_.empty())
|
||||||
{
|
{
|
||||||
auto &cmd = batchCommandsForWaitingResults_.front();
|
auto &cmd = batchCommandsForWaitingResults_.front();
|
||||||
cmd->exceptionCallback_(exceptPtr);
|
if (cmd->exceptionCallback_)
|
||||||
|
{
|
||||||
|
cmd->exceptionCallback_(exceptPtr);
|
||||||
|
}
|
||||||
batchCommandsForWaitingResults_.pop_front();
|
batchCommandsForWaitingResults_.pop_front();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -65,11 +65,14 @@ PgConnection::PgConnection(trantor::EventLoop *loop,
|
|||||||
[](PGconn *conn) { PQfinish(conn); })),
|
[](PGconn *conn) { PQfinish(conn); })),
|
||||||
channel_(loop, PQsocket(connectionPtr_.get()))
|
channel_(loop, PQsocket(connectionPtr_.get()))
|
||||||
{
|
{
|
||||||
|
if (channel_.fd() < 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR << "Failed to create Postgres connection";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PgConnection::init()
|
void PgConnection::init()
|
||||||
{
|
{
|
||||||
PQsetnonblocking(connectionPtr_.get(), 1);
|
|
||||||
if (channel_.fd() < 0)
|
if (channel_.fd() < 0)
|
||||||
{
|
{
|
||||||
LOG_ERROR << "Connection with Postgres could not be established";
|
LOG_ERROR << "Connection with Postgres could not be established";
|
||||||
@ -80,6 +83,8 @@ void PgConnection::init()
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PQsetnonblocking(connectionPtr_.get(), 1);
|
||||||
channel_.setReadCallback([this]() {
|
channel_.setReadCallback([this]() {
|
||||||
if (status_ == ConnectStatus::Bad)
|
if (status_ == ConnectStatus::Bad)
|
||||||
{
|
{
|
||||||
@ -128,6 +133,15 @@ void PgConnection::handleClosed()
|
|||||||
if (status_ == ConnectStatus::Bad)
|
if (status_ == ConnectStatus::Bad)
|
||||||
return;
|
return;
|
||||||
status_ = ConnectStatus::Bad;
|
status_ = ConnectStatus::Bad;
|
||||||
|
|
||||||
|
if (isWorking_)
|
||||||
|
{
|
||||||
|
// Connection was closed unexpectedly while isWorking_ was true.
|
||||||
|
isWorking_ = false;
|
||||||
|
handleFatalError();
|
||||||
|
callback_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
channel_.disableAll();
|
channel_.disableAll();
|
||||||
channel_.remove();
|
channel_.remove();
|
||||||
assert(closeCallback_);
|
assert(closeCallback_);
|
||||||
@ -142,8 +156,11 @@ void PgConnection::disconnect()
|
|||||||
auto thisPtr = shared_from_this();
|
auto thisPtr = shared_from_this();
|
||||||
loop_->runInLoop([thisPtr, &pro]() {
|
loop_->runInLoop([thisPtr, &pro]() {
|
||||||
thisPtr->status_ = ConnectStatus::Bad;
|
thisPtr->status_ = ConnectStatus::Bad;
|
||||||
thisPtr->channel_.disableAll();
|
if (thisPtr->channel_.fd() >= 0)
|
||||||
thisPtr->channel_.remove();
|
{
|
||||||
|
thisPtr->channel_.disableAll();
|
||||||
|
thisPtr->channel_.remove();
|
||||||
|
}
|
||||||
thisPtr->connectionPtr_.reset();
|
thisPtr->connectionPtr_.reset();
|
||||||
pro.set_value(1);
|
pro.set_value(1);
|
||||||
});
|
});
|
||||||
@ -398,9 +415,13 @@ void PgConnection::doAfterPreparing()
|
|||||||
|
|
||||||
void PgConnection::handleFatalError()
|
void PgConnection::handleFatalError()
|
||||||
{
|
{
|
||||||
auto exceptPtr =
|
if (exceptionCallback_)
|
||||||
std::make_exception_ptr(Failure(PQerrorMessage(connectionPtr_.get())));
|
{
|
||||||
exceptionCallback_(exceptPtr);
|
auto exceptPtr = std::make_exception_ptr(
|
||||||
|
Failure(PQerrorMessage(connectionPtr_.get())));
|
||||||
|
exceptionCallback_(exceptPtr);
|
||||||
|
}
|
||||||
|
|
||||||
exceptionCallback_ = nullptr;
|
exceptionCallback_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,11 +287,24 @@ DROGON_TEST(PostgreTest)
|
|||||||
FAULT("postgresql - DbClient streaming-type interface(8) what():",
|
FAULT("postgresql - DbClient streaming-type interface(8) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// 1.10 clean up
|
/// 1.10 query with raw parameter
|
||||||
|
auto rawParamData = std::make_shared<int>(htonl(3));
|
||||||
|
auto rawParam = RawParameter{rawParamData,
|
||||||
|
reinterpret_cast<char *>(rawParamData.get()),
|
||||||
|
sizeof(int),
|
||||||
|
1};
|
||||||
|
*clientPtr << "select * from users where length(user_id)=$1" << rawParam >>
|
||||||
|
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
|
||||||
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
|
FAULT("postgresql - DbClient streaming-type interface(9) what():",
|
||||||
|
e.base().what());
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 1.11 clean up
|
||||||
*clientPtr << "truncate table users restart identity" >>
|
*clientPtr << "truncate table users restart identity" >>
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); } >>
|
[TEST_CTX](const Result &r) { SUCCESS(); } >>
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("postgresql - DbClient streaming-type interface(9) what():",
|
FAULT("postgresql - DbClient streaming-type interface(10) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// Test asynchronous method
|
/// Test asynchronous method
|
||||||
@ -379,12 +392,21 @@ DROGON_TEST(PostgreTest)
|
|||||||
"postgresql1",
|
"postgresql1",
|
||||||
"pg",
|
"pg",
|
||||||
"postgresql");
|
"postgresql");
|
||||||
/// 2.6 clean up
|
/// 2.6 query with raw parameter
|
||||||
|
clientPtr->execSqlAsync(
|
||||||
|
"select * from users where length(user_id)=$1",
|
||||||
|
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
|
||||||
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
|
FAULT("postgresql - DbClient asynchronous interface(7) what():",
|
||||||
|
e.base().what());
|
||||||
|
},
|
||||||
|
rawParam);
|
||||||
|
/// 2.7 clean up
|
||||||
clientPtr->execSqlAsync(
|
clientPtr->execSqlAsync(
|
||||||
"truncate table users restart identity",
|
"truncate table users restart identity",
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); },
|
[TEST_CTX](const Result &r) { SUCCESS(); },
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("postgresql - DbClient asynchronous interface(7) what():",
|
FAULT("postgresql - DbClient asynchronous interface(8) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -464,7 +486,19 @@ DROGON_TEST(PostgreTest)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 3.6 clean up
|
/// 3.6 query with raw parameter
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto r = clientPtr->execSqlSync(
|
||||||
|
"select * from users where length(user_id)=$1", rawParam);
|
||||||
|
MANDATE(r.size() == 1);
|
||||||
|
}
|
||||||
|
catch (const DrogonDbException &e)
|
||||||
|
{
|
||||||
|
FAULT("postgresql - DbClient asynchronous interface(4) what():",
|
||||||
|
e.base().what());
|
||||||
|
}
|
||||||
|
/// 3.7 clean up
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto r =
|
auto r =
|
||||||
@ -557,7 +591,20 @@ DROGON_TEST(PostgreTest)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 4.6 clean up
|
/// 4.6 query with raw parameter
|
||||||
|
f = clientPtr->execSqlAsyncFuture(
|
||||||
|
"select * from users where length(user_id)=$1", rawParam);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto r = f.get();
|
||||||
|
MANDATE(r.size() == 1);
|
||||||
|
}
|
||||||
|
catch (const DrogonDbException &e)
|
||||||
|
{
|
||||||
|
FAULT("postgresql - DbClient future interface(4) what():",
|
||||||
|
e.base().what());
|
||||||
|
}
|
||||||
|
/// 4.7 clean up
|
||||||
f = clientPtr->execSqlAsyncFuture("truncate table users restart identity");
|
f = clientPtr->execSqlAsyncFuture("truncate table users restart identity");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1670,11 +1717,28 @@ DROGON_TEST(MySQLTest)
|
|||||||
FAULT("mysql - DbClient streaming-type interface(8) what():",
|
FAULT("mysql - DbClient streaming-type interface(8) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// 1.10 truncate
|
/// 1.10 query with raw parameter
|
||||||
|
// MariaDB uses little-endian, so the opposite of network ordering :P
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
auto rawParamData = std::make_shared<int>(3);
|
||||||
|
#else
|
||||||
|
auto rawParamData = std::make_shared<int>(0x03000000); // byteswapped 3
|
||||||
|
#endif
|
||||||
|
auto rawParam = RawParameter{rawParamData,
|
||||||
|
reinterpret_cast<char *>(rawParamData.get()),
|
||||||
|
sizeof(int),
|
||||||
|
internal::MySqlLong};
|
||||||
|
*clientPtr << "select * from users where length(user_id)=?" << rawParam >>
|
||||||
|
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
|
||||||
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
|
FAULT("mysql - DbClient streaming-type interface(9) what():",
|
||||||
|
e.base().what());
|
||||||
|
};
|
||||||
|
/// 1.11 truncate
|
||||||
*clientPtr << "truncate table users" >> [TEST_CTX](const Result &r) {
|
*clientPtr << "truncate table users" >> [TEST_CTX](const Result &r) {
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
} >> [TEST_CTX](const DrogonDbException &e) {
|
} >> [TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("mysql - DbClient streaming-type interface(9) what():",
|
FAULT("mysql - DbClient streaming-type interface(10) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// Test asynchronous method
|
/// Test asynchronous method
|
||||||
@ -1759,12 +1823,21 @@ DROGON_TEST(MySQLTest)
|
|||||||
"postgresql1",
|
"postgresql1",
|
||||||
"pg",
|
"pg",
|
||||||
"postgresql");
|
"postgresql");
|
||||||
/// 2.6 truncate
|
/// 2.6 query with raw parameter
|
||||||
|
clientPtr->execSqlAsync(
|
||||||
|
"select * from users where length(user_id)=?",
|
||||||
|
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
|
||||||
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
|
FAULT("mysql - DbClient asynchronous interface(7) what():",
|
||||||
|
e.base().what());
|
||||||
|
},
|
||||||
|
rawParam);
|
||||||
|
/// 2.7 truncate
|
||||||
clientPtr->execSqlAsync(
|
clientPtr->execSqlAsync(
|
||||||
"truncate table users",
|
"truncate table users",
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); },
|
[TEST_CTX](const Result &r) { SUCCESS(); },
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("mysql - DbClient asynchronous interface(7) what():",
|
FAULT("mysql - DbClient asynchronous interface(9) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1845,7 +1918,19 @@ DROGON_TEST(MySQLTest)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 3.6 truncate
|
/// 3.6 query with raw parameter
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto r = clientPtr->execSqlSync(
|
||||||
|
"select * from users where length(user_id)=?", rawParam);
|
||||||
|
MANDATE(r.size() == 1);
|
||||||
|
}
|
||||||
|
catch (const DrogonDbException &e)
|
||||||
|
{
|
||||||
|
FAULT("mysql - DbClient asynchronous interface(4) what():",
|
||||||
|
e.base().what());
|
||||||
|
}
|
||||||
|
/// 3.7 truncate
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto r = clientPtr->execSqlSync("truncate table users");
|
auto r = clientPtr->execSqlSync("truncate table users");
|
||||||
@ -1932,7 +2017,19 @@ DROGON_TEST(MySQLTest)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 4.6 truncate
|
/// 4.6. query with raw parameter
|
||||||
|
f = clientPtr->execSqlAsyncFuture(
|
||||||
|
"select * from users where length(user_id)=?", rawParam);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto r = f.get();
|
||||||
|
MANDATE(r.size() == 1);
|
||||||
|
}
|
||||||
|
catch (const DrogonDbException &e)
|
||||||
|
{
|
||||||
|
FAULT("mysql - DbClient future interface(5) what():", e.base().what());
|
||||||
|
}
|
||||||
|
/// 4.7 truncate
|
||||||
f = clientPtr->execSqlAsyncFuture("truncate table users");
|
f = clientPtr->execSqlAsyncFuture("truncate table users");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1941,7 +2038,7 @@ DROGON_TEST(MySQLTest)
|
|||||||
}
|
}
|
||||||
catch (const DrogonDbException &e)
|
catch (const DrogonDbException &e)
|
||||||
{
|
{
|
||||||
FAULT("mysql - DbClient future interface(5) what():", e.base().what());
|
FAULT("mysql - DbClient future interface(6) what():", e.base().what());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 5 Test Result and Row exception throwing
|
/// 5 Test Result and Row exception throwing
|
||||||
@ -2881,17 +2978,29 @@ DROGON_TEST(SQLite3Test)
|
|||||||
FAULT("sqlite3 - DbClient streaming-type interface(8) what():",
|
FAULT("sqlite3 - DbClient streaming-type interface(8) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// 1.10 clean up
|
/// 1.10 query with raw parameter
|
||||||
|
auto rawParamData = std::make_shared<int>(3);
|
||||||
|
auto rawParam = RawParameter{rawParamData,
|
||||||
|
reinterpret_cast<char *>(rawParamData.get()),
|
||||||
|
0,
|
||||||
|
Sqlite3TypeInt};
|
||||||
|
*clientPtr << "select * from users where length(user_id) = ?" << rawParam >>
|
||||||
|
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); } >>
|
||||||
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
|
FAULT("sqlite3 - DbClient streaming-type interface(9) what():",
|
||||||
|
e.base().what());
|
||||||
|
};
|
||||||
|
/// 1.11 clean up
|
||||||
*clientPtr << "delete from users" >> [TEST_CTX](const Result &r) {
|
*clientPtr << "delete from users" >> [TEST_CTX](const Result &r) {
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
} >> [TEST_CTX](const DrogonDbException &e) {
|
} >> [TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("sqlite3 - DbClient streaming-type interface(9.1) what():",
|
FAULT("sqlite3 - DbClient streaming-type interface(10.1) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
*clientPtr << "UPDATE sqlite_sequence SET seq = 0" >>
|
*clientPtr << "UPDATE sqlite_sequence SET seq = 0" >>
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); } >>
|
[TEST_CTX](const Result &r) { SUCCESS(); } >>
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("sqlite3 - DbClient streaming-type interface(9.2) what():",
|
FAULT("sqlite3 - DbClient streaming-type interface(10.2) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
};
|
};
|
||||||
/// Test asynchronous method
|
/// Test asynchronous method
|
||||||
@ -2975,19 +3084,28 @@ DROGON_TEST(SQLite3Test)
|
|||||||
"postgresql1",
|
"postgresql1",
|
||||||
"pg",
|
"pg",
|
||||||
"postgresql");
|
"postgresql");
|
||||||
/// 2.6 clean up
|
/// 2.6 query with raw parameter
|
||||||
|
clientPtr->execSqlAsync(
|
||||||
|
"select * from users where length(user_id) = ?",
|
||||||
|
[TEST_CTX](const Result &r) { MANDATE(r.size() == 1); },
|
||||||
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
|
FAULT("sqlite3 - DbClient asynchronous interface(7) what():",
|
||||||
|
e.base().what());
|
||||||
|
},
|
||||||
|
rawParam);
|
||||||
|
/// 2.7 clean up
|
||||||
clientPtr->execSqlAsync(
|
clientPtr->execSqlAsync(
|
||||||
"delete from users",
|
"delete from users",
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); },
|
[TEST_CTX](const Result &r) { SUCCESS(); },
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("sqlite3 - DbClient asynchronous interface(7.1) what():",
|
FAULT("sqlite3 - DbClient asynchronous interface(8.1) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
});
|
});
|
||||||
clientPtr->execSqlAsync(
|
clientPtr->execSqlAsync(
|
||||||
"UPDATE sqlite_sequence SET seq = 0",
|
"UPDATE sqlite_sequence SET seq = 0",
|
||||||
[TEST_CTX](const Result &r) { SUCCESS(); },
|
[TEST_CTX](const Result &r) { SUCCESS(); },
|
||||||
[TEST_CTX](const DrogonDbException &e) {
|
[TEST_CTX](const DrogonDbException &e) {
|
||||||
FAULT("sqlite3 - DbClient asynchronous interface(7.2) what():",
|
FAULT("sqlite3 - DbClient asynchronous interface(8.2) what():",
|
||||||
e.base().what());
|
e.base().what());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3074,7 +3192,19 @@ DROGON_TEST(SQLite3Test)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
/// 3.6 clean up
|
/// 3.6 query with raw parameter
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto r = clientPtr->execSqlSync(
|
||||||
|
"select * from users where length(user_id) = ?", rawParam);
|
||||||
|
MANDATE(r.size() == 1);
|
||||||
|
}
|
||||||
|
catch (const DrogonDbException &e)
|
||||||
|
{
|
||||||
|
FAULT("sqlite3 - DbClient asynchronous interface(4) what():",
|
||||||
|
e.base().what());
|
||||||
|
}
|
||||||
|
/// 3.7 clean up
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto r = clientPtr->execSqlSync("delete from users");
|
auto r = clientPtr->execSqlSync("delete from users");
|
||||||
@ -3177,6 +3307,19 @@ DROGON_TEST(SQLite3Test)
|
|||||||
{
|
{
|
||||||
SUCCESS();
|
SUCCESS();
|
||||||
}
|
}
|
||||||
|
/// 4.6 query with raw parameter
|
||||||
|
f = clientPtr->execSqlAsyncFuture(
|
||||||
|
"select * from users where length(user_id)=?", rawParam);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto r = f.get();
|
||||||
|
MANDATE(r.size() == 1);
|
||||||
|
}
|
||||||
|
catch (const DrogonDbException &e)
|
||||||
|
{
|
||||||
|
FAULT("sqlite3 - DbClient future interface(4) what():",
|
||||||
|
e.base().what());
|
||||||
|
}
|
||||||
/// 4.6 clean up
|
/// 4.6 clean up
|
||||||
f = clientPtr->execSqlAsyncFuture("delete from users");
|
f = clientPtr->execSqlAsyncFuture("delete from users");
|
||||||
try
|
try
|
||||||
|
2
trantor
2
trantor
@ -1 +1 @@
|
|||||||
Subproject commit 26ef5df161835751dd8473673e6f6362645e396e
|
Subproject commit 43fd79b2dbac59608a819ebba167e8fe2c079d90
|
Loading…
x
Reference in New Issue
Block a user