Merge pull request #9 from an-tao/with_orm

Add support for the model:
You can read and write the postgreSQL database using drogon:: orm::DbClient or drogon::orm::Transaction;
You can create a model class using 'drogon_ctl create model ...';
You can read and write models from database using the drogon::orm::Mapper <T> template;
Note: Currently only postgreSQL is supported.
This commit is contained in:
an-tao 2018-11-16 13:47:00 +08:00 committed by GitHub
commit c1164d8c42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
139 changed files with 6473 additions and 133 deletions

View File

@ -19,7 +19,7 @@ else()
set(CMAKE_CXX_STD_FLAGS c++17)
endif()
include_directories(${PROJECT_SOURCE_DIR}/trantor ${PROJECT_SOURCE_DIR}/lib/inc)
include_directories(${PROJECT_SOURCE_DIR}/trantor ${PROJECT_SOURCE_DIR}/lib/inc ${PROJECT_SOURCE_DIR}/orm_lib/inc)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake_modules/)
#jsoncpp
@ -61,6 +61,16 @@ 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)
set(USE_ORM TRUE)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE Release)
endif()
@ -80,6 +90,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})
@ -90,6 +101,13 @@ SET(CONFIG_HEADER "${PROJECT_SOURCE_DIR}/config.h")
file(WRITE "${CONFIG_HEADER}" "#pragma once\n")
file(APPEND "${CONFIG_HEADER}" "#include <trantor/utils/config.h>\n")
if(PostgreSQL_FOUND)
file(APPEND "${CONFIG_HEADER}" "#define USE_POSTGRESQL 1")
else()
file(APPEND "${CONFIG_HEADER}" "#define USE_POSTGRESQL 0")
endif()
string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER)
if(CMAKE_BUILD_TYPE_LOWER STREQUAL release)
file(APPEND "${CONFIG_HEADER}" "\n" "const char compileFlags[]=\"" ${CMAKE_CXX_FLAGS_RELEASE} "\";")
@ -113,14 +131,22 @@ 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)
#Installation
install(TARGETS drogon DESTINATION lib)
file(GLOB drogon_headers "${CMAKE_CURRENT_SOURCE_DIR}/lib/inc/drogon/*.h")
install(FILES ${drogon_headers} DESTINATION include/drogon)
if(USE_ORM)
file(GLOB orm_headers "${CMAKE_CURRENT_SOURCE_DIR}/orm_lib/inc/drogon/orm/*.h")
install(FILES ${orm_headers} DESTINATION include/drogon/orm)
endif()
file(GLOB drogon_util_headers "${CMAKE_CURRENT_SOURCE_DIR}/lib/inc/drogon/utils/*.h")
install(FILES ${drogon_util_headers}
DESTINATION include/drogon/utils)

54
COPYING Executable file
View File

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

View File

@ -26,6 +26,26 @@
"key": ""
}
],*/
"db_clients":[
{
//name:Name of the client,'default' by default
//"name":"",
//rdbms:server type, "postgreSQL" by default
"rdbms": "postgreSQL",
//host:server address,localhost by default
"host": "127.0.0.1",
//port:server port, 5432 by default
"port": 5432,
//dbname:Database name
"dbname": "test",
//user:'postgres' by default
"user": "",
//passwd:'' by default
"passwd": "",
//connection_number:1 by default
"connection_number":1
}
],
"app": {
//threads_num:num of threads,1 by default
"threads_num": 16,

View File

@ -14,6 +14,7 @@ foreach(cspFile ${SCP_LIST})
VERBATIM )
set(TEMPL_SRC ${TEMPL_SRC} ${classname}.cc)
endforeach()
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(drogon_ctl ${SRC_DIR} ${TEMPL_SRC})
add_dependencies(drogon_ctl trantor makeVersion _drogon_ctl)
install(TARGETS drogon_ctl DESTINATION bin)

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ class CommandHandler : public virtual drogon::DrObjectBase
virtual std::string script() { return ""; }
virtual std::string detail() { return ""; }
virtual ~CommandHandler() {}
};
};

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -49,4 +49,4 @@ void exeCommand(std::vector<std::string> &parameters)
{
std::cout << "command error!use help command to get usage!" << std::endl;
}
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -32,7 +32,9 @@ std::string create::detail()
"drogon_ctl create project <project_name> //"
"create a project named project_name\n"
"drogon_ctl create filter <class_name> //"
"create a filter named class_name\n";
"create a filter named class_name\n"
"drogon_ctl create model <model_path> //"
"create model classes in model_path\n";
}
void create::handleCommand(std::vector<std::string> &parameters)
@ -42,4 +44,4 @@ void create::handleCommand(std::vector<std::string> &parameters)
createObjName = std::string("create_") + createObjName;
parameters[0] = createObjName;
exeCommand(parameters);
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -1,14 +1,13 @@
/**
*
* @file
* @author An Tao
* @section LICENSE
* create_controller.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
* Drogon
*
*/

View File

@ -49,4 +49,4 @@ void create_filter::handleCommand(std::vector<std::string> &parameters)
auto className = parameters[0];
createFilterHeaderFile(className);
createFilterSourceFile(className);
}
}

View File

@ -31,4 +31,4 @@ class create_filter : public DrObject<create_filter>, public CommandHandler
void newViewHeaderFile(std::ofstream &file, const std::string &className);
void newViewSourceFile(std::ofstream &file, const std::string &className, std::ifstream &infile);
};
} // namespace drogon_ctl
} // namespace drogon_ctl

405
drogon_ctl/create_model.cc Normal file
View File

@ -0,0 +1,405 @@
/**
*
* create_model.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.
*
* Drogon
*
*/
#include "create_model.h"
#include "cmd.h"
#include <drogon/config.h>
#include <drogon/utils/Utilities.h>
#include <drogon/HttpViewData.h>
#include <drogon/DrTemplateBase.h>
#include <json/json.h>
#include <iostream>
#include <fstream>
#include <regex>
#include <unistd.h>
#include <dirent.h>
#include <dlfcn.h>
#include <fstream>
#include <unistd.h>
using namespace drogon_ctl;
std::string nameTransform(const std::string &origName, bool isType)
{
auto str = origName;
std::transform(str.begin(), str.end(), str.begin(), tolower);
std::string::size_type startPos = 0;
std::string::size_type pos;
std::string ret;
do
{
pos = str.find("_", startPos);
if (pos != std::string::npos)
ret += str.substr(startPos, pos - startPos);
else
{
ret += str.substr(startPos);
break;
}
while (str[pos] == '_')
pos++;
if (str[pos] >= 'a' && str[pos] <= 'z')
str[pos] += ('A' - 'a');
startPos = pos;
} while (1);
if (isType && ret[0] >= 'a' && ret[0] <= 'z')
ret[0] += ('A' - 'a');
return ret;
}
#if USE_POSTGRESQL
void create_model::createModelClassFromPG(const std::string &path, const DbClientPtr &client, const std::string &tableName)
{
auto className = nameTransform(tableName, true);
HttpViewData data;
data["className"] = className;
data["tableName"] = tableName;
data["hasPrimaryKey"] = (int)0;
data["primaryKeyName"] = "";
data["dbName"] = _dbname;
std::vector<ColumnInfo> cols;
*client << "SELECT * \
FROM information_schema.columns \
WHERE table_schema = 'public' \
AND table_name = $1"
<< tableName << Mode::Blocking >>
[&](const Result &r) {
for (auto row : r)
{
ColumnInfo info;
info._dbType = "pg";
info._colName = row["column_name"].as<std::string>();
info._colTypeName = nameTransform(info._colName, true);
info._colValName = nameTransform(info._colName, false);
auto isNullAble = row["is_nullable"].as<std::string>();
info._notNull = isNullAble == "YES" ? false : true;
auto type = row["data_type"].as<std::string>();
info._colDatabaseType = type;
if (type == "smallint")
{
info._colType = "short";
info._colLength = 2;
}
else if (type == "integer")
{
info._colType = "int32_t";
info._colLength = 4;
}
else if (type == "bigint" || type == "numeric") //FIXME:Use int64 to represent numeric type?
{
info._colType = "int64_t";
info._colLength = 8;
}
else if (type == "real")
{
info._colType = "float";
info._colLength = sizeof(float);
}
else if (type == "double precision")
{
info._colType = "double";
info._colLength = sizeof(double);
}
else if (type == "character varying")
{
info._colType = "std::string";
if (!row["character_maximum_length"].isNull())
info._colLength = row["character_maximum_length"].as<ssize_t>();
}
else if (type == "boolean")
{
info._colType = "bool";
info._colLength = 1;
}
else if (type == "date")
{
info._colType = "::trantor::Date";
info._colLength = 4;
}
else if (type.find("timestamp") != std::string::npos)
{
info._colType = "::trantor::Date";
}
else
{
info._colType = "std::string";
//FIXME add more type such as hstore,blob...
}
auto defaultVal = row["column_default"].as<std::string>();
if (!defaultVal.empty())
{
info._hasDefaultVal = true;
if (defaultVal.find("nextval(") == 0)
{
info._isAutoVal = true;
}
}
cols.push_back(std::move(info));
}
} >>
[](const DrogonDbException &e) {
std::cerr << e.base().what() << std::endl;
exit(1);
};
size_t pkNumber = 0;
*client << "SELECT \
pg_constraint.conname AS pk_name,\
pg_constraint.conkey AS pk_vector \
FROM pg_constraint \
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
WHERE \
pg_class.relname = $1 \
AND pg_constraint.contype = 'p'"
<< tableName
<< Mode::Blocking >>
[&](bool isNull, const std::string &pkName, const std::vector<std::shared_ptr<short>> &pk) {
if (!isNull)
{
//std::cout << tableName << " Primary key = " << pk.size() << std::endl;
pkNumber = pk.size();
}
} >>
[](const DrogonDbException &e) {
std::cerr << e.base().what() << std::endl;
exit(1);
};
data["hasPrimaryKey"] = (int)pkNumber;
if (pkNumber == 1)
{
*client << "SELECT \
pg_attribute.attname AS colname,\
pg_type.typname AS typename,\
pg_constraint.contype AS contype \
FROM pg_constraint \
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid \
AND pg_attribute.attnum = pg_constraint.conkey [ 1 ] \
INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid \
WHERE pg_class.relname = $1 and pg_constraint.contype='p'"
<< tableName << Mode::Blocking >>
[&](bool isNull, std::string colName, const std::string &type) {
if (isNull)
return;
data["primaryKeyName"] = colName;
for (auto &col : cols)
{
if (col._colName == colName)
{
col._isPrimaryKey = true;
data["primaryKeyType"] = col._colType;
}
}
} >>
[](const DrogonDbException &e) {
std::cerr << e.base().what() << std::endl;
exit(1);
};
}
else if (pkNumber > 1)
{
std::vector<std::string> pkNames, pkTypes;
for (size_t i = 1; i <= pkNumber; i++)
{
*client << "SELECT \
pg_attribute.attname AS colname,\
pg_type.typname AS typename,\
pg_constraint.contype AS contype \
FROM pg_constraint \
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid \
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid \
AND pg_attribute.attnum = pg_constraint.conkey [ $1 ] \
INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid \
WHERE pg_class.relname = $2 and pg_constraint.contype='p'"
<< (int)i
<< tableName
<< Mode::Blocking >>
[&](bool isNull, std::string colName, const std::string &type) {
if (isNull)
return;
//std::cout << "primary key name=" << colName << std::endl;
pkNames.push_back(colName);
for (auto &col : cols)
{
if (col._colName == colName)
{
col._isPrimaryKey = true;
pkTypes.push_back(col._colType);
}
}
} >>
[](const DrogonDbException &e) {
std::cerr << e.base().what() << std::endl;
exit(1);
};
}
data["primaryKeyName"] = pkNames;
data["primaryKeyType"] = pkTypes;
}
data["columns"] = cols;
std::ofstream headerFile(path + "/" + className + ".h", std::ofstream::out);
std::ofstream sourceFile(path + "/" + className + ".cc", std::ofstream::out);
auto templ = DrTemplateBase::newTemplate("model_h.csp");
headerFile << templ->genText(data);
templ = DrTemplateBase::newTemplate("model_cc.csp");
sourceFile << templ->genText(data);
}
void create_model::createModelFromPG(const std::string &path, const DbClientPtr &client)
{
*client << "SELECT a.oid,"
"a.relname AS name,"
"b.description AS comment "
"FROM pg_class a "
"LEFT OUTER JOIN pg_description b ON b.objsubid = 0 AND a.oid = b.objoid "
"WHERE a.relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public') "
"AND a.relkind = 'r' ORDER BY a.relname"
<< Mode::Blocking >>
[&](bool isNull, size_t oid, const std::string &tableName, const std::string &comment) {
if (!isNull)
{
std::cout << "table name:" << tableName << std::endl;
createModelClassFromPG(path, client, tableName);
}
} >>
[](const DrogonDbException &e) {
std::cerr << e.base().what() << std::endl;
exit(1);
};
}
#endif
void create_model::createModel(const std::string &path, const Json::Value &config)
{
auto dbType = config.get("rdbms", "No dbms").asString();
if (dbType == "postgreSQL")
{
#if USE_POSTGRESQL
std::cout << "postgresql" << std::endl;
auto host = config.get("host", "127.0.0.1").asString();
auto port = config.get("port", 5432).asUInt();
auto dbname = config.get("dbname", "").asString();
if (dbname == "")
{
std::cerr << "Please configure dbname in " << path << "/model.json " << std::endl;
exit(1);
}
_dbname = dbname;
auto user = config.get("user", "").asString();
if (user == "")
{
std::cerr << "Please configure user in " << path << "/model.json " << std::endl;
exit(1);
}
auto password = config.get("passwd", "").asString();
auto connStr = formattedString("host=%s port=%u dbname=%s user=%s", host.c_str(), port, dbname.c_str(), user.c_str());
if (!password.empty())
{
connStr += " password=";
connStr += password;
}
DbClientPtr client = drogon::orm::DbClient::newPgClient(connStr, 1);
std::cout << "Connect to server..." << std::endl;
std::cout << "Source files in the " << path << " folder will be overwritten, continue(y/n)?\n";
auto in = getchar();
if (in != 'Y' && in != 'y')
{
std::cout << "Abort!" << std::endl;
exit(0);
}
auto tables = config["tables"];
if (!tables || tables.size() == 0)
createModelFromPG(path, client);
else
{
for (int i = 0; i < (int)tables.size(); i++)
{
auto tableName = tables[i].asString();
std::cout << "table name:" << tableName << std::endl;
createModelClassFromPG(path, client, tableName);
}
}
#endif
}
else if (dbType == "No dbms")
{
std::cerr << "Please configure Model in " << path << "/model.json " << std::endl;
exit(1);
}
else
{
std::cerr << "Does not support " << dbType << std::endl;
exit(1);
}
}
void create_model::createModel(const std::string &path)
{
DIR *dp;
if ((dp = opendir(path.c_str())) == NULL)
{
std::cerr << "No such file or directory : " << path << std::endl;
return;
}
closedir(dp);
auto configFile = path + "/model.json";
if (access(configFile.c_str(), 0) != 0)
{
std::cerr << "Config file " << configFile << " not found!" << std::endl;
exit(1);
}
if (access(configFile.c_str(), R_OK) != 0)
{
std::cerr << "No permission to read config file " << configFile << std::endl;
exit(1);
}
std::ifstream infile(configFile.c_str(), std::ifstream::in);
if (infile)
{
Json::Value configJsonRoot;
try
{
infile >> configJsonRoot;
createModel(path, configJsonRoot);
}
catch (const std::exception &exception)
{
std::cerr << "Configuration file format error! in " << configFile << ":" << std::endl;
std::cerr << exception.what() << std::endl;
exit(1);
}
}
}
void create_model::handleCommand(std::vector<std::string> &parameters)
{
#if USE_POSTGRESQL
std::cout << "Create model" << std::endl;
if (parameters.size() == 0)
{
std::cerr << "Missing Model path name!" << std::endl;
}
for (auto path : parameters)
{
createModel(path);
}
#else
std::cout << "No database can be found in your system, please install one first!" << std::endl;
exit(1);
#endif
}

62
drogon_ctl/create_model.h Normal file
View File

@ -0,0 +1,62 @@
/**
*
* create_model.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.
*
* Drogon
*
*/
#pragma once
#include <drogon/config.h>
#include <json/json.h>
#if USE_POSTGRESQL
#include <drogon/orm/DbClient.h>
using namespace drogon::orm;
#endif
#include <drogon/DrObject.h>
#include "CommandHandler.h"
#include <string>
using namespace drogon;
namespace drogon_ctl
{
struct ColumnInfo
{
std::string _colName;
std::string _colValName;
std::string _colTypeName;
std::string _colType;
std::string _colDatabaseType;
std::string _dbType;
ssize_t _colLength = -1;
bool _isAutoVal = false;
bool _isPrimaryKey = false;
bool _notNull = false;
bool _hasDefaultVal = false;
};
class create_model : public DrObject<create_model>, public CommandHandler
{
public:
virtual void handleCommand(std::vector<std::string> &parameters) override;
virtual std::string script() override { return "create Model classes files"; }
protected:
void createModel(const std::string &path);
void createModel(const std::string &path, const Json::Value &config);
#if USE_POSTGRESQL
void createModelClassFromPG(const std::string &path, const DbClientPtr &client, const std::string &tableName);
void createModelFromPG(const std::string &path, const DbClientPtr &client);
#endif
std::string _dbname;
};
} // namespace drogon_ctl

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -67,6 +67,11 @@ static void newConfigFile(std::ofstream &configFile)
auto templ=DrTemplateBase::newTemplate("config");
configFile << templ->genText();
}
static void newModelConfigFile(std::ofstream &configFile)
{
auto templ = DrTemplateBase::newTemplate("model_json");
configFile << templ->genText();
}
void create_project::createProject(const std::string &projectName)
{
@ -88,6 +93,7 @@ void create_project::createProject(const std::string &projectName)
mkdir("controllers", 0755);
mkdir("filters", 0755);
mkdir("build", 0755);
mkdir("models", 0755);
mkdir("cmake_modules", 0755);
std::ofstream jsonFile("cmake_modules/FindJsoncpp.cmake", std::ofstream::out);
newJsonFindFile(jsonFile);
@ -98,4 +104,6 @@ void create_project::createProject(const std::string &projectName)
newGitIgFile(gitFile);
std::ofstream configFile("config.json", std::ofstream::out);
newConfigFile(configFile);
std::ofstream modelConfigFile("models/model.json", std::ofstream::out);
newModelConfigFile(modelConfigFile);
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#pragma once
@ -28,4 +28,4 @@ class create_project : public DrObject<create_project>, public CommandHandler
std::string _outputPath = ".";
void createProject(const std::string &projectName);
};
} // namespace drogon_ctl
} // namespace drogon_ctl

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -32,4 +32,4 @@ class create_view : public DrObject<create_view>, public CommandHandler
void newViewHeaderFile(std::ofstream &file, const std::string &className);
void newViewSourceFile(std::ofstream &file, const std::string &className, std::ifstream &infile);
};
} // namespace drogon_ctl
} // namespace drogon_ctl

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -60,4 +60,4 @@ void help::handleCommand(std::vector<std::string> &parameters)
}
}
}
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
* this is a drogon lib tool program
* run drogon_ctl help to get usage
*/
@ -36,4 +36,4 @@ int main(int argc, char *argv[])
exeCommand(args);
return 0;
}
}

View File

@ -52,9 +52,18 @@ find_package(ZLIB REQUIRED)
include_directories(${ZLIB_INCLUDE_DIR})
link_libraries(${ZLIB_LIBRARIES})
#find postgres
find_package(PostgreSQL)
if(PostgreSQL_FOUND)
include_directories(${PostgreSQL_INCLUDE_DIR})
link_libraries(${PostgreSQL_LIBRARIES})
endif()
AUX_SOURCE_DIRECTORY(./ SRC_DIR)
AUX_SOURCE_DIRECTORY(controllers CTL_SRC)
AUX_SOURCE_DIRECTORY(filters FILTER_SRC)
AUX_SOURCE_DIRECTORY(models MODEL_SRC)
include_directories(/usr/local/include)
@ -71,4 +80,5 @@ foreach(cspFile ${SCP_LIST})
set(VIEWSRC ${VIEWSRC} ${classname}.cc)
endforeach()
add_executable({{ProjectName}} ${SRC_DIR} ${CTL_SRC} ${FILTER_SRC} ${VIEWSRC})
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable({{ProjectName}} ${SRC_DIR} ${CTL_SRC} ${FILTER_SRC} ${VIEWSRC} ${MODEL_SRC})

View File

@ -26,6 +26,27 @@
"key": ""
}
],*/
/*
"db_clients": [
{
//name:Name of the client,'default' by default
//"name":"",
//rdbms:server type, "postgreSQL" by default
"rdbms": "postgreSQL",
//host:server address,localhost by default
"host": "127.0.0.1",
//port:server port, 5432 by default
"port": 5432,
//dbname:Database name
"dbname": "test",
//user:'postgres' by default
"user": "",
//passwd:'' by default
"passwd": "",
//connection_number:1 by default
"connection_number": 1
}
],*/
"app": {
//threads_num:num of threads,1 by default
"threads_num": 16,
@ -89,18 +110,23 @@
"use_gzip": true,
//static_files_cache_time:5 (seconds) by default,the time in which static file response is cached,
//0 means cache forever,the negative value means no cache
"static_files_cache_time":5,
"static_files_cache_time": 5,
//simple_controllers_map:Configuring mapping from path to simple controller
"simple_controllers_map": [
{
"path": "/path/name",
"controller": "controllerClassName",
"http_methods": ["get","post"],
"filters": ["FilterClassName"]
"http_methods": [
"get",
"post"
],
"filters": [
"FilterClassName"
]
}
],
//idle_connection_timeout: defaults to 60 seconds, the lifetime
//of the connection without read or write
"idle_connection_timeout":60
"idle_connection_timeout": 60
}
}
}

View File

@ -0,0 +1,272 @@
/**
*
* {{className}}.cc
* created by drogon_ctl
*
*/
<%inc#include "create_model.h"
using namespace drogon_ctl;
%>
#include "{{className}}.h"
#include <string>
<%c++
auto cols=@@.get<std::vector<ColumnInfo>>("columns");
auto className=@@.get<std::string>("className");
%>
using namespace drogon_model::{{dbName}};
<%c++for(auto col:cols){
%>
const std::string {{className}}::Cols::<%c++$$<<col._colName;%> = "<%c++$$<<col._colName;%>";
<%c++
}%>
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
const std::string {{className}}::primaryKeyName = "{{primaryKeyName}}";
<%c++}else{%>
const std::vector<std::string> {{className}}::primaryKeyName = {<%c++
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
for(size_t i=0;i<pkName.size();i++)
{
$$<<"\""<<pkName[i]<<"\"";
if(i<(pkName.size()-1))
$$<<",";
}
%>};
<%c++}%>
<%c++ if(@@.get<int>("hasPrimaryKey",0)>0){%>
const bool {{className}}::hasPrimaryKey = true;
<%c++ }else{%>
const bool {{className}}::hasPrimaryKey = false;
<%c++}%>
const std::string {{className}}::tableName = "{{tableName}}";
const std::vector<typename {{className}}::MetaData> {{className}}::_metaData={
<%c++for(size_t i=0;i<cols.size();i++){
auto &col=cols[i];
$$<<"{\""<<col._colName<<"\",\""<<col._colType<<"\",\""<<col._colDatabaseType<<"\","<<col._colLength<<","<<col._isAutoVal<<","<<col._isPrimaryKey<<","<<col._notNull<<"}";
if(i<(cols.size()-1))
$$<<",\n";
else
$$<<"\n";
}%>
};
const std::string &{{className}}::getColumnName(size_t index) noexcept(false)
{
assert(index < _metaData.size());
return _metaData[index]._colName;
}
{{className}}::{{className}}(const Row &r) noexcept
{
<%c++
for(auto col:cols)
{
if(col._colType.empty())
continue;
%>
if(!r["<%c++$$<<col._colName;%>"].isNull())
{
<%c++
if(col._colDatabaseType=="date")
{
$$<<" auto daysStr = r[\""<<col._colName<<"\"].as<std::string>();\n";
$$<<" struct tm stm;\n";
$$<<" memset(&stm,0,sizeof(stm));\n";
$$<<" strptime(daysStr.c_str(),\"%Y-%m-%d\",&stm);\n";
$$<<" long t = timelocal(&stm);\n";
// $$<<" _"<<col._colValName<<"=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\n";
$$<<" _"<<col._colValName<<"=std::make_shared<::trantor::Date>(t*1000000);\n";
$$<<" }\n";
continue;
}
else if(col._colDatabaseType.find("timestamp")!=std::string::npos)
{
$$<<" auto timeStr = r[\""<<col._colName<<"\"].as<std::string>();\n";
$$<<" struct tm stm;\n";
$$<<" memset(&stm,0,sizeof(stm));\n";
$$<<" auto p = strptime(timeStr.c_str(),\"%Y-%m-%d %H:%M:%S\",&stm);\n";
$$<<" size_t t = timelocal(&stm);\n";
$$<<" size_t decimalNum = 0;\n";
$$<<" if(*p=='.')\n";
$$<<" {\n";
$$<<" std::string decimals(p+1,&timeStr[timeStr.length()]);\n";
$$<<" while(decimals.length()<6)\n";
$$<<" {\n";
$$<<" decimals += \"0\";\n";
$$<<" }\n";
$$<<" decimalNum = (size_t)atol(decimals.c_str());\n";
$$<<" }\n";
// $$<<" _"<<col._colValName<<"=std::make_shared<::trantor::Date>(::trantor::Date(946656000000000).after(daysNum*86400));\n";
$$<<" _"<<col._colValName<<"=std::make_shared<::trantor::Date>(t*1000000+decimalNum);\n";
$$<<" }\n";
continue;
}
%>
_<%c++$$<<col._colValName;%>=std::make_shared<<%c++$$<<col._colType;%>>(r["<%c++$$<<col._colName;%>"].as<<%c++$$<<col._colType;%>>());
}
<%c++
}
%>
}
<%c++
for(size_t i=0;i<cols.size();i++)
{
auto & col = cols[i];
if(!col._colType.empty())
{
$$<<"const "<<col._colType<<" & "<<className<<"::getValueOf"<<col._colTypeName<<"(const "<<col._colType<<" &defaultValue) const noexcept\n";
$$<<"{\n";
$$<<" if(_"<<col._colValName<<")\n";
$$<<" return *_"<<col._colValName<<";\n";
$$<<" return defaultValue;\n";
$$<<"}\n";
$$<<"std::shared_ptr<const "<<col._colType<<"> "<<className<<"::get"<<col._colTypeName<<"() const noexcept\n";
$$<<"{\n";
$$<<" return _"<<col._colValName<<";\n";
$$<<"}\n";
if(!col._isAutoVal)
{
$$<<"void "<<className<<"::set"<<col._colTypeName<<"(const "<<col._colType<<" &"<<col._colValName<<") noexcept\n";
$$<<"{\n";
if(col._colDatabaseType=="date")
{
$$<<" _"<<col._colValName<<" = std::make_shared<"<<col._colType<<">("<<col._colValName<<".roundDay());\n";
}
else
{
$$<<" _"<<col._colValName<<" = std::make_shared<"<<col._colType<<">("<<col._colValName<<");\n";
}
$$<<" _dirtyFlag["<<i<<"] = true;\n";
$$<<"}\n";
if(col._colType=="std::string")
{
$$<<"void "<<className<<"::set"<<col._colTypeName<<"("<<col._colType<<" &&"<<col._colValName<<") noexcept\n";
$$<<"{\n";
$$<<" _"<<col._colValName<<" = std::make_shared<"<<col._colType<<">(std::move("<<col._colValName<<"));\n";
$$<<" _dirtyFlag["<<i<<"] = true;\n";
$$<<"}\n";
}
}
if(col._isPrimaryKey&&@@.get<int>("hasPrimaryKey")==1)
{
$$<<"const typename "<<className<<"::PrimaryKeyType & "<<className<<"::getPrimaryKey() const\n";
$$<<"{\n";
$$<<" assert(_"<<col._colValName<<");\n";
$$<<" return *_"<<col._colValName<<";\n";
$$<<"}\n";
}
}
$$<<"\n";
}
if(@@.get<int>("hasPrimaryKey")>1)
{
$$<<"typename "<<className<<"::PrimaryKeyType "<<className<<"::getPrimaryKey() const\n";
$$<<"{\n";
$$<<" return std::make_tuple(";
int count=0;
for(auto col:cols)
{
if(col._isPrimaryKey)
{
count++;
$$<<"*_"<<col._colValName;
if(count<@@.get<int>("hasPrimaryKey"))
$$<<",";
}
}
$$<<");\n";
$$<<"}\n";
}
%>
const std::vector<std::string> &{{className}}::insertColumns() noexcept
{
static const std::vector<std::string> _inCols={
<%c++for(size_t i=0;i<cols.size();i++){
auto col=cols[i];
if(!col._isAutoVal&&!col._colType.empty())
{
$$<<" \""<<col._colName<<"\"";
if(i<(cols.size()-1))
$$<<",\n";
else
$$<<"\n";
}
}%>
};
return _inCols;
}
void {{className}}::outputArgs(drogon::orm::internal::SqlBinder &binder) const
{
<%c++for(auto col:cols){
if(!col._isAutoVal&&!col._colType.empty())
{
%>
if(get<%c++$$<<col._colTypeName;%>())
{
<%c++if(col._colDatabaseType=="date"){%>
binder << (int32_t)(getValueOf<%c++$$<<col._colTypeName;%>().microSecondsSinceEpoch()/1000000-946656000)/86400;
<%c++}else if(col._colDatabaseType.find("timestamp")!=std::string::npos){%>
binder << get<%c++$$<<col._colTypeName;%>()->toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true);
<%c++}else{%>
binder << getValueOf<%c++$$<<col._colTypeName;%>();
<%c++}%>
}
else
{
binder << nullptr;
}
<%c++
}
}
%>
}
const std::vector<std::string> {{className}}::updateColumns() const
{
std::vector<std::string> ret;
for(size_t i=0;i<sizeof(_dirtyFlag);i++)
{
if(_dirtyFlag[i])
{
ret.push_back(getColumnName(i));
}
}
return ret;
}
void {{className}}::updateArgs(drogon::orm::internal::SqlBinder &binder) const
{
<%c++
for(size_t i=0;i<cols.size();i++)
{
auto & col=cols[i];
if(col._colType.empty()||col._isAutoVal)
continue;
%>
if(_dirtyFlag[<%c++$$<<i;%>])
{
if(get<%c++$$<<col._colTypeName;%>())
{
<%c++if(col._colDatabaseType=="date"){%>
binder << (int32_t)(getValueOf<%c++$$<<col._colTypeName;%>().microSecondsSinceEpoch()/1000000-946656000)/86400;
<%c++}else if(col._colDatabaseType.find("timestamp")!=std::string::npos){%>
binder << get<%c++$$<<col._colTypeName;%>()->toCustomedFormattedStringLocal("%Y-%m-%d %H:%M:%S", true);
<%c++}else{%>
binder << getValueOf<%c++$$<<col._colTypeName;%>();
<%c++}%>
}
else
{
binder << nullptr;
}
}
<%c++
}
%>
}

View File

@ -0,0 +1,130 @@
/**
*
* {{className}}.h
* Created by drogon_ctl
*
*/
<%inc#include "create_model.h"
using namespace drogon_ctl;
%>
#pragma once
#include <drogon/orm/Result.h>
#include <drogon/orm/Row.h>
#include <drogon/orm/Field.h>
#include <drogon/orm/SqlBinder.h>
#include <trantor/utils/Date.h>
#include <string>
#include <memory>
#include <vector>
#include <tuple>
#include <stdint.h>
#include <iostream>
using namespace drogon::orm;
namespace drogon_model
{
namespace {{dbName}}
{
class {{className}}
{
public:
struct Cols
{
<%c++
auto cols=@@.get<std::vector<ColumnInfo>>("columns");
for(size_t i=0;i<cols.size();i++)
{
$$<<" static const std::string "<<cols[i]._colName<<";\n";
}
%>
};
const static int primaryKeyNumber;
const static std::string tableName;
const static bool hasPrimaryKey;
<%c++if(@@.get<int>("hasPrimaryKey")<=1){%>
const static std::string primaryKeyName;
<%c++if(!@@.get<std::string>("primaryKeyType").empty()){%>
typedef {{primaryKeyType}} PrimaryKeyType;
const PrimaryKeyType & getPrimaryKey() const;
<%c++}else{%>
typedef void PrimaryKeyType;
<%c++}%>
<%c++}else{
auto pkTypes=@@.get<std::vector<std::string>>("primaryKeyType");
std::string typelist;
for(size_t i=0;i<pkTypes.size();i++)
{
typelist += pkTypes[i];
if(i<(pkTypes.size()-1))
typelist += ",";
}
%>
const static std::vector<std::string> primaryKeyName;
typedef std::tuple<<%c++$$<<typelist;%>> PrimaryKeyType;//<%c++
auto pkName=@@.get<std::vector<std::string>>("primaryKeyName");
for(size_t i=0;i<pkName.size();i++)
{
$$<<pkName[i];
if(i<(pkName.size()-1))
$$<<",";
}
%>
PrimaryKeyType getPrimaryKey() const;
<%c++}%>
{{className}}(const Row &r) noexcept;
<%c++
for(auto col:cols)
{
$$<<" //For column "<<col._colName<<"\n";
if(!col._colType.empty())
{
$$<<" const "<<col._colType<<" &getValueOf"<<col._colTypeName<<"(const "<<col._colType<<" &defaultValue="<<col._colType<<"()) const noexcept;\n";
$$<<" std::shared_ptr<const "<<col._colType<<"> get"<<col._colTypeName<<"() const noexcept;\n";
if(!col._isAutoVal)
{
$$<<" void set"<<col._colTypeName<<"(const "<<col._colType<<" &"<<col._colValName<<") noexcept;\n";
if(col._colType=="std::string")
$$<<" void set"<<col._colTypeName<<"("<<col._colType<<" &&"<<col._colValName<<") noexcept;\n";
}
}
else
$$<<" //FIXME!!"<<" getValueOf"<<col._colTypeName<<"() const noexcept;\n";
$$<<"\n";
}
%>
static const std::string &getColumnName(size_t index) noexcept(false);
static const std::vector<std::string> &insertColumns() noexcept;
void outputArgs(drogon::orm::internal::SqlBinder &binder) const;
const std::vector<std::string> updateColumns() const;
void updateArgs(drogon::orm::internal::SqlBinder &binder) const;
private:
<%c++
for(auto col:cols)
{
if(!col._colType.empty())
$$<<" std::shared_ptr<"<<col._colType<<"> _"<<col._colValName<<";\n";
}
%>
struct MetaData
{
const std::string _colName;
const std::string _colType;
const std::string _colDatabaseType;
const ssize_t _colLength;
const bool _isAutoVal;
const bool _isPrimaryKey;
const bool _notNull;
};
static const std::vector<MetaData> _metaData;
bool _dirtyFlag[<%c++$$<<cols.size();%>]={ false };
};
} // namespace {{dbName}}
} // namespace drogon_model

View File

@ -0,0 +1,13 @@
{
//rdbms:server type, postgreSQL
"rdbms":"postgreSQL",
//host:server address,localhost by default;
"host":"127.0.0.1",
//port:server port, 5432 by default;
"port":5432,
//dbname:Database name;
"dbname":"",
"user":"",
"passwd":"",
"tables":[]
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -32,4 +32,4 @@ void version::handleCommand(std::vector<std::string> &parameters)
std::cout << "version:" << VERSION << std::endl;
std::cout << "git commit:" << VERSION_MD5 << std::endl;
std::cout << "compile config:" << compileFlags <<" "<< includeDirs << std::endl;
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -16,4 +16,4 @@ void JsonTestController::asyncHandleHttpRequest(const HttpRequestPtr &req, const
json["rows"] = array;
auto resp = HttpResponse::newHttpJsonResponse(json);
callback(resp);
}
}

View File

@ -12,4 +12,4 @@ class JsonTestController : public drogon::HttpSimpleController<JsonTestControlle
PATH_LIST_BEGIN
PATH_ADD("/json", "drogon::GetFilter", "drogon::LocalHostFilter");
PATH_LIST_END
};
};

View File

@ -7,4 +7,4 @@ void ListParaCtl::asyncHandleHttpRequest(const HttpRequestPtr &req, const std::f
data.insert("parameters", req->getParameters());
auto res = drogon::HttpResponse::newHttpViewResponse("ListParaView.csp", data);
callback(res);
}
}

View File

@ -7,4 +7,4 @@ void TestController::asyncHandleHttpRequest(const HttpRequestPtr &req, const std
resp->setBody("<p>Hello, world!</p>");
resp->setExpiredTime(0);
callback(resp);
}
}

View File

@ -6,4 +6,4 @@ void TestViewCtl::asyncHandleHttpRequest(const HttpRequestPtr &req, const std::f
data.insert("title", std::string("TestView"));
auto res = drogon::HttpResponse::newHttpViewResponse("TestView", data);
callback(res);
}
}

View File

@ -13,4 +13,4 @@ void WebSocketTest::handleNewConnection(const HttpRequestPtr &,
const WebSocketConnectionPtr &)
{
LOG_TRACE << "new websocket connection!";
}
}

View File

@ -28,4 +28,4 @@ void Attachment::upload(const HttpRequestPtr &req,
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(HttpResponse::k200OK);
callback(resp);
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#pragma once
@ -44,4 +44,4 @@ class Cookie
std::string _key;
std::string _value;
};
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ class DeleteFilter : public HttpFilter<DeleteFilter>
const FilterCallback &fcb,
const FilterChainCallback &fccb) override;
};
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ class GetFilter : public HttpFilter<GetFilter>
const FilterCallback &fcb,
const FilterChainCallback &fccb) override;
};
} // namespace drogon
} // namespace drogon

View File

@ -8,13 +8,16 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#pragma once
#include <drogon/config.h>
#if USE_POSTGRESQL
#include <drogon/orm/DbClient.h>
#endif
#include <drogon/utils/Utilities.h>
#include <drogon/HttpBinder.h>
#include <trantor/utils/NonCopyable.h>
@ -137,6 +140,17 @@ class HttpAppFramework : public trantor::NonCopyable
virtual void setStaticFilesCacheTime(int cacheTime) = 0;
virtual int staticFilesCacheTime() const = 0;
virtual void setIdleConnectionTimeout(size_t timeout) = 0;
#if USE_POSTGRESQL
virtual orm::DbClientPtr getDbClient(const std::string &name = "default") = 0;
virtual void createDbClient(const std::string &dbType,
const std::string &host,
const u_short port,
const std::string &databaseName,
const std::string &userName,
const std::string &password,
const size_t connectionNum = 1,
const std::string &name = "default") = 0;
#endif
private:
virtual void registerHttpController(const std::string &pathPattern,

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -73,4 +73,4 @@ public:
protected:
HttpClient() = default;
};
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -37,4 +37,4 @@ class HttpFilter : public DrObject<T>, public HttpFilterBase
public:
virtual ~HttpFilter() {}
};
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -70,4 +70,4 @@ class HttpRequest
static HttpRequestPtr newHttpRequest();
};
} // namespace drogon
} // namespace drogon

View File

@ -18,7 +18,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ class InnerIpFilter : public HttpFilter<InnerIpFilter>
const FilterCallback &fcb,
const FilterChainCallback &fccb) override;
};
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ class LocalHostFilter : public HttpFilter<LocalHostFilter>
const FilterCallback &fcb,
const FilterChainCallback &fccb) override;
};
} // namespace drogon
} // namespace drogon

View File

@ -7,4 +7,4 @@ class NotFound : public DrTemplate<NotFound>
NotFound(){};
virtual ~NotFound(){};
virtual std::string genText(const HttpViewData &) override;
};
};

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ class PostFilter : public HttpFilter<PostFilter>
const FilterCallback &fcb,
const FilterChainCallback &fccb) override;
};
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ class PutFilter : public HttpFilter<PutFilter>
const FilterCallback &fcb,
const FilterChainCallback &fccb) override;
};
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -43,4 +43,4 @@ class WebSocketConnection
virtual any *getMutableContext() = 0;
};
typedef std::shared_ptr<WebSocketConnection> WebSocketConnectionPtr;
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -7,7 +7,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -28,4 +28,4 @@
#include <drogon/HttpClient.h>
#include <drogon/utils/Utilities.h>
#endif
#endif

View File

@ -7,7 +7,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -216,6 +216,30 @@ static void loadApp(const Json::Value &app)
auto kickOffTimeout = app.get("idle_connection_timeout", 60).asUInt64();
HttpAppFramework::instance().setIdleConnectionTimeout(kickOffTimeout);
}
static void loadDbClients(const Json::Value &dbClients)
{
#if USE_POSTGRESQL
if (!dbClients)
return;
for (auto &client : dbClients)
{
auto type = client.get("rdbms", "postgreSQL").asString();
auto host = client.get("host", "127.0.0.1").asString();
auto port = client.get("port", 5432).asUInt();
auto dbname = client.get("dbname", "").asString();
if (dbname == "")
{
std::cerr << "Please configure dbname in the configuration file" << std::endl;
exit(1);
}
auto user = client.get("user", "postgres").asString();
auto password = client.get("passwd", "").asString();
auto connNum = client.get("connection_number", 1).asUInt();
auto name = client.get("name", "default").asString();
HttpAppFramework::instance().createDbClient(type, host, (u_short)port, dbname, user, password, connNum, name);
}
#endif
}
static void loadListeners(const Json::Value &listeners)
{
if (!listeners)
@ -246,4 +270,5 @@ void ConfigLoader::load()
loadApp(_configJsonRoot["app"]);
loadSSL(_configJsonRoot["ssl"]);
loadListeners(_configJsonRoot["listeners"]);
loadDbClients(_configJsonRoot["db_clients"]);
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -31,4 +31,4 @@ class ConfigLoader : public trantor::NonCopyable
std::string _configFile;
Json::Value _configJsonRoot;
};
} // namespace drogon
} // namespace drogon

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -50,4 +50,4 @@ const std::string Cookie::cookieString() const
ret.resize(ret.length() - 2); //delete last semicolon
ret.append("\r\n");
return ret;
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -46,4 +46,4 @@ std::unordered_map<std::string, DrAllocFunc> &DrClassMap::getMap()
{
static std::unordered_map<std::string, DrAllocFunc> map;
return map;
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#include <drogon/DrTemplateBase.h>

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#include "HttpAppFrameworkImpl.h"
@ -360,7 +360,7 @@ void HttpAppFrameworkImpl::run()
break;
}
waitpid(child_pid, &child_status, 0);
sleep(5);
sleep(1);
LOG_INFO << "start new process";
}
}
@ -1215,3 +1215,32 @@ HttpAppFramework &HttpAppFramework::instance()
HttpAppFramework::~HttpAppFramework()
{
}
#if USE_POSTGRESQL
orm::DbClientPtr HttpAppFrameworkImpl::getDbClient(const std::string &name)
{
return _dbClientsMap[name];
}
void HttpAppFrameworkImpl::createDbClient(const std::string &dbType,
const std::string &host,
const u_short port,
const std::string &databaseName,
const std::string &userName,
const std::string &password,
const size_t connectionNum,
const std::string &name)
{
if (dbType == "postgreSQL")
{
auto connStr = formattedString("host=%s port=%u dbname=%s user=%s", host.c_str(), port, databaseName.c_str(), userName.c_str());
if (!password.empty())
{
connStr += " password=";
connStr += password;
}
auto client = drogon::orm::DbClient::newPgClient(connStr, connectionNum);
_dbClientsMap[name] = client;
}
}
#endif

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#pragma once
@ -85,7 +85,17 @@ class HttpAppFrameworkImpl : public HttpAppFramework
}
trantor::EventLoop *loop();
#if USE_POSTGRESQL
virtual orm::DbClientPtr getDbClient(const std::string &name = "default") override;
virtual void createDbClient(const std::string &dbType,
const std::string &host,
const u_short port,
const std::string &databaseName,
const std::string &userName,
const std::string &password,
const size_t connectionNum = 1,
const std::string &name = "default") override;
#endif
private:
virtual void registerHttpController(const std::string &pathPattern,
const HttpBinderBasePtr &binder,
@ -202,5 +212,9 @@ class HttpAppFrameworkImpl : public HttpAppFramework
int _staticFilesCacheTime = 5;
std::unordered_map<std::string, std::weak_ptr<HttpResponse>> _staticFilesCache;
std::mutex _staticFilesCacheMutex;
#if USE_POSTGRESQL
std::map<std::string, orm::DbClientPtr> _dbClientsMap;
#endif
};
} // namespace drogon

View File

@ -19,7 +19,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -20,7 +20,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#pragma once

View File

@ -240,4 +240,4 @@ HttpClientPtr HttpClient::newHttpClient(const std::string &ip, uint16_t port, bo
HttpClientPtr HttpClient::newHttpClient(const std::string &hostString)
{
return std::make_shared<HttpClientImpl>(((HttpAppFrameworkImpl &)(HttpAppFramework::instance())).loop(), hostString);
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#pragma once
@ -39,4 +39,4 @@ class HttpClientImpl : public HttpClient, public std::enable_shared_from_this<Ht
void onRecvMessage(const trantor::TcpConnectionPtr &, trantor::MsgBuffer *);
std::string _domain;
};
} // namespace drogon
} // namespace drogon

View File

@ -19,7 +19,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -19,7 +19,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -16,7 +16,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -20,7 +20,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -18,7 +18,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -19,7 +19,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -20,7 +20,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#pragma once

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#include <drogon/HttpViewBase.h>

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ void InnerIpFilter::doFilter(const HttpRequestPtr &req,
}
auto res = drogon::HttpResponse::newNotFoundResponse();
fcb(res);
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
@ -26,4 +26,4 @@ void LocalHostFilter::doFilter(const HttpRequestPtr &req,
}
auto res = drogon::HttpResponse::newNotFoundResponse();
fcb(res);
}
}

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -8,7 +8,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/
#pragma once
@ -38,4 +38,4 @@ class SharedLibManager : public trantor::NonCopyable
void *loadLibs(const std::string &sourceFile, void *oldHld);
trantor::TimerId _timeId;
};
} // namespace drogon
} // namespace drogon

View File

@ -7,7 +7,7 @@
* Use of this source code is governed by a MIT license
* that can be found in the License file.
*
* @section DESCRIPTION
* Drogon
*
*/

View File

@ -118,4 +118,4 @@ const any &WebSocketConnectionImpl::WebSocketConnectionImpl::getContext() const
any *WebSocketConnectionImpl::WebSocketConnectionImpl::getMutableContext()
{
return &_context;
}
}

View File

@ -41,4 +41,4 @@ class WebSocketConnectionImpl : public WebSocketConnection
WebSocketControllerBasePtr _ctrlPtr;
any _context;
};
} // namespace drogon
} // namespace drogon

View File

@ -249,4 +249,4 @@ std::string Md5Encode::Encode(std::string src_info)
result.append(GetHexStr(param.uc_));
result.append(GetHexStr(param.ud_));
return result;
}
}

View File

@ -70,4 +70,4 @@ class Md5Encode
static const unsigned long long k_ti_num_integer;
};
#endif
#endif

View File

@ -0,0 +1,73 @@
/** Handling of SQL arrays.
*
* DO NOT INCLUDE THIS FILE DIRECTLY; include drogon/orm/Field.h instead.
*
* Copyright (c) 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
#pragma once
#include <stdexcept>
#include <string>
#include <utility>
namespace drogon
{
namespace orm
{
/// Low-level array parser.
/** Use this to read an array field retrieved from the database.
*
* Use this only if your client encoding is UTF-8, ASCII, or a single-byte
* encoding which is a superset of ASCII.
*
* The input is a C-style string containing the textual representation of an
* array, as returned by the database. The parser reads this representation
* on the fly. The string must remain in memory until parsing is done.
*
* Parse the array by making calls to @c get_next until it returns a
* @c juncture of "done". The @c juncture tells you what the parser found in
* that step: did the array "nest" to a deeper level, or "un-nest" back up?
*/
class ArrayParser
{
public:
/// What's the latest thing found in the array?
enum juncture
{
/// Starting a new row.
row_start,
/// Ending the current row.
row_end,
/// Found a NULL value.
null_value,
/// Found a string value.
string_value,
/// Parsing has completed.
done,
};
/// Constructor. You don't need this; use @c field::as_array instead.
explicit ArrayParser(const char input[]);
/// Parse the next step in the array.
/** Returns what it found. If the juncture is @c string_value, the string
* will contain the value. Otherwise, it will be empty.
*
* Call this until the @c juncture it returns is @c done.
*/
std::pair<juncture, std::string> getNext();
private:
/// Current parsing position in the input.
const char *m_pos;
};
} // namespace orm
} // namespace drogon

Some files were not shown because too many files have changed in this diff Show More