Add a way to set the character set when creating DbClient objects (#486)
This commit is contained in:
parent
56b5d03fed
commit
3424d3f2c4
|
@ -47,6 +47,9 @@
|
|||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//client_encoding: The character set used by the client. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//connection_number: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"connection_number": 1
|
||||
|
@ -114,7 +117,7 @@
|
|||
//is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
|
||||
"is_recursive": true,
|
||||
//filters: string array, the filters applied to the location.
|
||||
"filters":[]
|
||||
"filters": []
|
||||
}],
|
||||
//max_connections: maximum connections number,100000 by default
|
||||
"max_connections": 100000,
|
||||
|
@ -227,8 +230,11 @@
|
|||
}],
|
||||
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
||||
"custom_config": {
|
||||
"realm" : "drogonRealm",
|
||||
"opaque" : "drogonOpaque",
|
||||
"credentials" : [ {"user" : "drogon", "password": "dr0g0n"} ]
|
||||
"realm": "drogonRealm",
|
||||
"opaque": "drogonOpaque",
|
||||
"credentials": [{
|
||||
"user": "drogon",
|
||||
"password": "dr0g0n"
|
||||
}]
|
||||
}
|
||||
}
|
|
@ -722,6 +722,12 @@ void create_model::createModel(const std::string &path,
|
|||
connStr += " password=";
|
||||
connStr += password;
|
||||
}
|
||||
auto characterSet = config.get("client_encoding", "").asString();
|
||||
if (!characterSet.empty())
|
||||
{
|
||||
connStr += " client_encoding=";
|
||||
connStr += characterSet;
|
||||
}
|
||||
|
||||
auto schema = config.get("schema", "public").asString();
|
||||
DbClientPtr client = drogon::orm::DbClient::newPgClient(connStr, 1);
|
||||
|
@ -822,6 +828,12 @@ void create_model::createModel(const std::string &path,
|
|||
connStr += " password=";
|
||||
connStr += password;
|
||||
}
|
||||
auto characterSet = config.get("client_encoding", "").asString();
|
||||
if (!characterSet.empty())
|
||||
{
|
||||
connStr += " client_encoding=";
|
||||
connStr += characterSet;
|
||||
}
|
||||
DbClientPtr client = drogon::orm::DbClient::newMysqlClient(connStr, 1);
|
||||
std::cout << "Connect to server..." << std::endl;
|
||||
if (forceOverwrite_)
|
||||
|
|
|
@ -47,6 +47,9 @@
|
|||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
||||
//any synchronous interface of it.
|
||||
"is_fast": false,
|
||||
//client_encoding: The character set used by the client. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//connection_number: 1 by default, if the 'is_fast' is true, the number is the number of
|
||||
//connections per IO thread, otherwise it is the total number of all connections.
|
||||
"connection_number": 1
|
||||
|
@ -114,7 +117,7 @@
|
|||
//is_recursive: true by default. If it is set to false, files in sub directories can't be accessed.
|
||||
"is_recursive": true,
|
||||
//filters: string array, the filters applied to the location.
|
||||
"filters":[]
|
||||
"filters": []
|
||||
}],
|
||||
//max_connections: maximum connections number,100000 by default
|
||||
"max_connections": 100000,
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
"schema": "public",
|
||||
//user: User name
|
||||
"user": "",
|
||||
//passwd: Password
|
||||
"passwd": "",
|
||||
//password or passwd: Password
|
||||
"password": "",
|
||||
//client_encoding: The character set used by drogon_ctl. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized.
|
||||
"tables": [],
|
||||
"relationships": {
|
||||
|
|
|
@ -1092,7 +1092,8 @@ class HttpAppFramework : public trantor::NonCopyable
|
|||
const size_t connectionNum = 1,
|
||||
const std::string &filename = "",
|
||||
const std::string &name = "default",
|
||||
const bool isFast = false) = 0;
|
||||
const bool isFast = false,
|
||||
const std::string &characterSet = "") = 0;
|
||||
|
||||
/// Get the DNS resolver
|
||||
/**
|
||||
|
|
|
@ -470,6 +470,11 @@ static void loadDbClients(const Json::Value &dbClients)
|
|||
auto name = client.get("name", "default").asString();
|
||||
auto filename = client.get("filename", "").asString();
|
||||
auto isFast = client.get("is_fast", false).asBool();
|
||||
auto characterSet = client.get("characterSet", "").asString();
|
||||
if (characterSet.empty())
|
||||
{
|
||||
characterSet = client.get("client_encoding", "").asString();
|
||||
}
|
||||
drogon::app().createDbClient(type,
|
||||
host,
|
||||
(unsigned short)port,
|
||||
|
@ -479,7 +484,8 @@ static void loadDbClients(const Json::Value &dbClients)
|
|||
connNum,
|
||||
filename,
|
||||
name,
|
||||
isFast);
|
||||
isFast,
|
||||
characterSet);
|
||||
}
|
||||
}
|
||||
static void loadListeners(const Json::Value &listeners)
|
||||
|
|
|
@ -51,7 +51,8 @@ class DbClientManager : public trantor::NonCopyable
|
|||
const size_t connectionNum,
|
||||
const std::string &filename,
|
||||
const std::string &name,
|
||||
const bool isFast);
|
||||
const bool isFast,
|
||||
const std::string &characterSet);
|
||||
bool areAllDbClientsAvailable() const noexcept;
|
||||
|
||||
private:
|
||||
|
|
|
@ -36,7 +36,8 @@ void DbClientManager::createDbClient(const std::string &dbType,
|
|||
const size_t connectionNum,
|
||||
const std::string &filename,
|
||||
const std::string &name,
|
||||
const bool isFast)
|
||||
const bool isFast,
|
||||
const std::string &characterSet)
|
||||
{
|
||||
LOG_FATAL << "No database is supported by drogon, please install the "
|
||||
"database development library first.";
|
||||
|
|
|
@ -886,7 +886,8 @@ HttpAppFramework &HttpAppFrameworkImpl::createDbClient(
|
|||
const size_t connectionNum,
|
||||
const std::string &filename,
|
||||
const std::string &name,
|
||||
const bool isFast)
|
||||
const bool isFast,
|
||||
const std::string &characterSet)
|
||||
{
|
||||
assert(!running_);
|
||||
dbClientManagerPtr_->createDbClient(dbType,
|
||||
|
@ -898,7 +899,8 @@ HttpAppFramework &HttpAppFrameworkImpl::createDbClient(
|
|||
connectionNum,
|
||||
filename,
|
||||
name,
|
||||
isFast);
|
||||
isFast,
|
||||
characterSet);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -425,7 +425,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework
|
|||
const size_t connectionNum = 1,
|
||||
const std::string &filename = "",
|
||||
const std::string &name = "default",
|
||||
const bool isFast = false) override;
|
||||
const bool isFast = false,
|
||||
const std::string &characterSet = "") override;
|
||||
|
||||
inline static HttpAppFrameworkImpl &instance()
|
||||
{
|
||||
|
|
|
@ -59,6 +59,7 @@ class DbClient : public trantor::NonCopyable
|
|||
* as the operating system name of the user running the application.
|
||||
* - password: Password to be used if the server demands password
|
||||
* authentication.
|
||||
* - client_encoding: The character set to be used on database connections.
|
||||
*
|
||||
* For other key words on PostgreSQL, see the PostgreSQL documentation.
|
||||
* Only a pair of key values is valid for Sqlite3, and its keyword is
|
||||
|
|
|
@ -94,7 +94,8 @@ void DbClientManager::createDbClient(const std::string &dbType,
|
|||
const size_t connectionNum,
|
||||
const std::string &filename,
|
||||
const std::string &name,
|
||||
const bool isFast)
|
||||
const bool isFast,
|
||||
const std::string &characterSet)
|
||||
{
|
||||
auto connStr = utils::formattedString("host=%s port=%u dbname=%s user=%s",
|
||||
host.c_str(),
|
||||
|
@ -108,6 +109,11 @@ void DbClientManager::createDbClient(const std::string &dbType,
|
|||
}
|
||||
std::string type = dbType;
|
||||
std::transform(type.begin(), type.end(), type.begin(), tolower);
|
||||
if (!characterSet.empty())
|
||||
{
|
||||
connStr += " client_encoding=";
|
||||
connStr += characterSet;
|
||||
}
|
||||
DbInfo info;
|
||||
info.connectionInfo_ = connStr;
|
||||
info.connectionNumber_ = connectionNum;
|
||||
|
|
|
@ -40,6 +40,7 @@ enum class ConnectStatus
|
|||
{
|
||||
None = 0,
|
||||
Connecting,
|
||||
SettingCharacterSet,
|
||||
Ok,
|
||||
Bad
|
||||
};
|
||||
|
|
|
@ -94,6 +94,10 @@ MysqlConnection::MysqlConnection(trantor::EventLoop *loop,
|
|||
{
|
||||
passwd_ = value;
|
||||
}
|
||||
else if (key == "client_encoding")
|
||||
{
|
||||
characterSet_ = value;
|
||||
}
|
||||
}
|
||||
loop_->queueInLoop([this]() {
|
||||
MYSQL *ret;
|
||||
|
@ -200,15 +204,27 @@ void MysqlConnection::handleTimeout()
|
|||
return;
|
||||
}
|
||||
// I don't think the programe can run to here.
|
||||
status_ = ConnectStatus::Ok;
|
||||
if (okCallback_)
|
||||
if (characterSet_.empty())
|
||||
{
|
||||
auto thisPtr = shared_from_this();
|
||||
okCallback_(thisPtr);
|
||||
status_ = ConnectStatus::Ok;
|
||||
if (okCallback_)
|
||||
{
|
||||
auto thisPtr = shared_from_this();
|
||||
okCallback_(thisPtr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
startSetCharacterSet();
|
||||
return;
|
||||
}
|
||||
}
|
||||
setChannel();
|
||||
}
|
||||
else if (status_ == ConnectStatus::SettingCharacterSet)
|
||||
{
|
||||
continueSetCharacterSet(status);
|
||||
}
|
||||
else if (status_ == ConnectStatus::Ok)
|
||||
{
|
||||
}
|
||||
|
@ -241,11 +257,19 @@ void MysqlConnection::handleEvent()
|
|||
handleClosed();
|
||||
return;
|
||||
}
|
||||
status_ = ConnectStatus::Ok;
|
||||
if (okCallback_)
|
||||
if (characterSet_.empty())
|
||||
{
|
||||
auto thisPtr = shared_from_this();
|
||||
okCallback_(thisPtr);
|
||||
status_ = ConnectStatus::Ok;
|
||||
if (okCallback_)
|
||||
{
|
||||
auto thisPtr = shared_from_this();
|
||||
okCallback_(thisPtr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
startSetCharacterSet();
|
||||
return;
|
||||
}
|
||||
}
|
||||
setChannel();
|
||||
|
@ -320,8 +344,62 @@ void MysqlConnection::handleEvent()
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (status_ == ConnectStatus::SettingCharacterSet)
|
||||
{
|
||||
continueSetCharacterSet(status);
|
||||
}
|
||||
}
|
||||
void MysqlConnection::continueSetCharacterSet(int status)
|
||||
{
|
||||
int err;
|
||||
waitStatus_ = mysql_set_character_set_cont(&err, mysqlPtr_.get(), status);
|
||||
if (waitStatus_ == 0)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
LOG_ERROR << "Error(" << err << ") \""
|
||||
<< mysql_error(mysqlPtr_.get()) << "\"";
|
||||
LOG_ERROR << "Failed to mysql_real_connect()";
|
||||
handleClosed();
|
||||
return;
|
||||
}
|
||||
status_ = ConnectStatus::Ok;
|
||||
if (okCallback_)
|
||||
{
|
||||
auto thisPtr = shared_from_this();
|
||||
okCallback_(thisPtr);
|
||||
}
|
||||
}
|
||||
setChannel();
|
||||
}
|
||||
void MysqlConnection::startSetCharacterSet()
|
||||
{
|
||||
int err;
|
||||
waitStatus_ = mysql_set_character_set_start(&err,
|
||||
mysqlPtr_.get(),
|
||||
characterSet_.data());
|
||||
if (waitStatus_ == 0)
|
||||
{
|
||||
if (err)
|
||||
{
|
||||
LOG_ERROR << "error";
|
||||
loop_->queueInLoop(
|
||||
[thisPtr = shared_from_this()] { thisPtr->outputError(); });
|
||||
return;
|
||||
}
|
||||
status_ = ConnectStatus::Ok;
|
||||
if (okCallback_)
|
||||
{
|
||||
auto thisPtr = shared_from_this();
|
||||
okCallback_(thisPtr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status_ = ConnectStatus::SettingCharacterSet;
|
||||
}
|
||||
setChannel();
|
||||
}
|
||||
|
||||
void MysqlConnection::execSqlInLoop(
|
||||
string_view &&sql,
|
||||
size_t paraNum,
|
||||
|
|
|
@ -96,10 +96,11 @@ class MysqlConnection : public DbConnection,
|
|||
std::vector<int> &&format,
|
||||
ResultCallback &&rcb,
|
||||
std::function<void(const std::exception_ptr &)> &&exceptCallback);
|
||||
|
||||
void startSetCharacterSet();
|
||||
void continueSetCharacterSet(int status);
|
||||
std::unique_ptr<trantor::Channel> channelPtr_;
|
||||
std::shared_ptr<MYSQL> mysqlPtr_;
|
||||
|
||||
std::string characterSet_;
|
||||
void handleTimeout();
|
||||
|
||||
void handleClosed();
|
||||
|
|
|
@ -1967,15 +1967,17 @@ int main(int argc, char *argv[])
|
|||
trantor::Logger::setLogLevel(trantor::Logger::kDebug);
|
||||
#if USE_POSTGRESQL
|
||||
auto postgre_client = DbClient::newPgClient(
|
||||
"host=127.0.0.1 port=5432 dbname=postgres user=postgres", 1);
|
||||
"host=127.0.0.1 port=5432 dbname=postgres user=postgres "
|
||||
"client_encoding=utf8",
|
||||
1);
|
||||
while (!postgre_client->hasAvailableConnections())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
#endif
|
||||
#if USE_MYSQL
|
||||
auto mysql_client =
|
||||
DbClient::newMysqlClient("host=localhost port=3306 user=root", 1);
|
||||
auto mysql_client = DbClient::newMysqlClient(
|
||||
"host=localhost port=3306 user=root client_encoding=utf8mb4", 1);
|
||||
while (!mysql_client->hasAvailableConnections())
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
|
|
Loading…
Reference in New Issue