MT#62868 Add timeout to database queries

This patch adds timeout to the queries to avoid SEMS waiting forever for a response.

Change-Id: I6933caf36fbeb00211386fb6a3415f9f739f7300
mr14.1
Donat Zenichev 9 months ago
parent b52ef41f35
commit 23325e3913

@ -18,11 +18,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "DBUtils.h"
#include "DBRegAgent.h"
#include "AmSession.h"
#include "AmEventDispatcher.h"
@ -194,9 +195,14 @@ int DBRegAgent::onLoad()
try {
MainDBConnection.set_option(new mysqlpp::ReconnectOption(true));
// matched instead of changed rows in result, so we know when to create DB entry
MainDBConnection.set_option(new mysqlpp::FoundRowsOption(true));
std::initializer_list<DBArgOption> args = {
dbReconnectOption, // if present = dbReconnectOption(true)
dbConnectTimeoutOption(5), // seconds
dbReadTimeoutOption(5), // seconds
dbWriteTimeoutOption(5), // seconds
dbFoundRowsOption // if present = dbFoundRowsOption(true)
};
apply_mysql_options(MainDBConnection, args);
MainDBConnection.connect(mysql_db.c_str(), mysql_server.c_str(),
mysql_user.c_str(), mysql_passwd.c_str());
if (!MainDBConnection) {
@ -204,9 +210,7 @@ int DBRegAgent::onLoad()
return -1;
}
ProcessorDBConnection.set_option(new mysqlpp::ReconnectOption(true));
// matched instead of changed rows in result, so we know when to create DB entry
ProcessorDBConnection.set_option(new mysqlpp::FoundRowsOption(true));
apply_mysql_options(ProcessorDBConnection, args);
ProcessorDBConnection.connect(mysql_db.c_str(), mysql_server.c_str(),
mysql_user.c_str(), mysql_passwd.c_str());
if (!ProcessorDBConnection) {
@ -615,14 +619,14 @@ void DBRegAgent::createRegistration(long object_id,
if (NULL != uac_auth_i) {
DBG("REGISTER: Enabling UAC Auth for new registration of type: <%s>\n", TYPE_TO_STRING(type));
// get a sessionEventHandler from uac_auth
AmArg di_args, ret;
AmArg a;
a.setBorrowedPointer(reg);
di_args.push(a);
di_args.push(a);
uac_auth_i->invoke("getHandler", di_args, ret);
if (!ret.size()) {
ERROR("Can not add auth handler to new registration!\n");
@ -814,7 +818,7 @@ void DBRegAgent::process(AmEvent* ev) {
if (ev->event_id == E_SYSTEM) {
AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(ev);
if(sys_ev){
if(sys_ev){
DBG("Session received system Event\n");
if (sys_ev->sys_event == AmSystemEvent::ServerShutdown) {
stop();
@ -951,7 +955,7 @@ void DBRegAgent::createDBRegistration(long object_id, const regType type, mysqlp
WARN("creating registration in DB with query '%s' failed: '%s', type: %s\n",
insert_query.c_str(), res.info(), TYPE_TO_STRING(type));
}
} catch (const mysqlpp::Exception& er) {
} catch (const mysqlpp::Exception& er) {
// Catch-all for any MySQL++ exceptions
ERROR("MySQL++ error: %s\n", er.what());
return;
@ -979,7 +983,7 @@ void DBRegAgent::deleteDBRegistration(long object_id, const regType type, mysqlp
WARN("removing registration in DB with query '%s' failed: '%s', type: %s\n",
insert_query.c_str(), res.info(), TYPE_TO_STRING(type));
}
} catch (const mysqlpp::Exception& er) {
} catch (const mysqlpp::Exception& er) {
// Catch-all for any MySQL++ exceptions
ERROR("MySQL++ error: %s\n", er.what());
return;
@ -1005,8 +1009,7 @@ void DBRegAgent::updateDBRegistration(mysqlpp::Connection& db_connection,
}
if (update_ts) {
query << ", last_registration=NOW(), "
"expiry=TIMESTAMPADD(SECOND,"+int2str(expiry)+", NOW())";
query << ", last_registration=NOW(), expiry=TIMESTAMPADD(SECOND," + int2str(expiry) + ", NOW())";
}
if (update_contacts) {
@ -1024,21 +1027,19 @@ void DBRegAgent::updateDBRegistration(mysqlpp::Connection& db_connection,
mysqlpp::SimpleResult res = query.execute();
if (!res) {
WARN("updating registration in DB with query '%s' failed: '%s'\n",
query_str.c_str(), res.info());
WARN("updating registration in DB with query '%s' failed: '%s'\n", query_str.c_str(), res.info());
} else {
if (!res.rows()) {
// should not happen - DB entry is created on load or on createRegistration
DBG("creating registration DB entry for subscriber %ld, type: %s\n", object_id, TYPE_TO_STRING(type));
createDBRegistration(object_id, type, db_connection);
query.reset();
query << query_str;
mysqlpp::SimpleResult res = query.execute();
if (!res || !res.rows()) {
WARN("updating registration in DB with query '%s' failed: '%s'\n",
query_str.c_str(), res.info());
}
// should not happen - DB entry is created on load or on createRegistration
DBG("creating registration DB entry for subscriber %ld, type: %s\n", object_id, TYPE_TO_STRING(type));
createDBRegistration(object_id, type, db_connection);
query.reset();
query << query_str;
mysqlpp::SimpleResult res = query.execute();
if (!res || !res.rows()) {
WARN("updating registration in DB with query '%s' failed: '%s'\n", query_str.c_str(), res.info());
}
}
}
@ -1054,7 +1055,7 @@ void DBRegAgent::updateDBRegistration(mysqlpp::Connection& db_connection,
void DBRegAgent::onSipReplyEvent(AmSipReplyEvent* ev) {
if (!ev) return;
DBG("received SIP reply event for '%s'\n",
DBG("received SIP reply event for '%s'\n",
#ifdef HAS_OFFER_ANSWER
ev->reply.from_tag.c_str()
#else
@ -1220,7 +1221,7 @@ void DBRegAgent::onSipReplyEvent(AmSipReplyEvent* ev) {
void DBRegAgent::run() {
DBG("DBRegAgent thread: waiting 2 sec for server startup ...\n");
sleep(2);
mysqlpp::Connection::thread_start();
if (enable_ratelimiting) {
@ -1380,7 +1381,7 @@ void DBRegAgent::setRegistrationTimer(long object_id,
"minimum_reregister_interval=%f)\n",
t_expiry_min, t_expiry_max, reg_start_ts, expiry,
reregister_interval, minimum_reregister_interval);
registration_scheduler.insert_timer_abs(timer, t_expiry_min * 1000000, t_expiry_max * 1000000);
}
} else {
@ -1735,7 +1736,7 @@ void DBRegAgentProcessorThread::rateLimitWait() {
memcpy(&last_check, &current, sizeof(struct timeval));
double seconds_passed = (double)time_passed.tv_sec +
(double)time_passed.tv_usec / 1000000.0;
allowance += seconds_passed *
allowance += seconds_passed *
(double) DBRegAgent::ratelimit_rate / (double)DBRegAgent::ratelimit_per;
if (allowance > (double)DBRegAgent::ratelimit_rate)
@ -1756,7 +1757,7 @@ void DBRegAgentProcessorThread::rateLimitWait() {
void DBRegAgentProcessorThread::run() {
DBG("DBRegAgentProcessorThread thread started\n");
// register us as SIP event receiver for MOD_NAME_processor
AmEventDispatcher::instance()->addEventQueue(MOD_NAME "_processor",this);
@ -1780,14 +1781,14 @@ void DBRegAgentProcessorThread::run() {
mysqlpp::Connection::thread_end();
DBG("DBRegAgentProcessorThread thread stopped\n");
DBG("DBRegAgentProcessorThread thread stopped\n");
}
void DBRegAgentProcessorThread::process(AmEvent* ev) {
if (ev->event_id == E_SYSTEM) {
AmSystemEvent* sys_ev = dynamic_cast<AmSystemEvent*>(ev);
if(sys_ev){
if(sys_ev){
DBG("Session received system Event\n");
if (sys_ev->sys_event == AmSystemEvent::ServerShutdown) {
DBG("stopping processor thread\n");
@ -1822,32 +1823,32 @@ void DBRegAgent::run_tests() {
gettimeofday(&now, 0);
RegTimer rt;
rt.expires = now.tv_sec + 10;
rt.expires = now.tv_sec + 10;
rt.cb=test_cb;
registration_scheduler.insert_timer(&rt);
RegTimer rt2;
rt2.expires = now.tv_sec + 5;
rt2.expires = now.tv_sec + 5;
rt2.cb=test_cb;
registration_scheduler.insert_timer(&rt2);
RegTimer rt3;
rt3.expires = now.tv_sec + 15;
rt3.expires = now.tv_sec + 15;
rt3.cb=test_cb;
registration_scheduler.insert_timer(&rt3);
RegTimer rt4;
rt4.expires = now.tv_sec - 1;
rt4.expires = now.tv_sec - 1;
rt4.cb=test_cb;
registration_scheduler.insert_timer(&rt4);
RegTimer rt5;
rt5.expires = now.tv_sec + 100000;
rt5.expires = now.tv_sec + 100000;
rt5.cb=test_cb;
registration_scheduler.insert_timer(&rt5);
RegTimer rt6;
rt6.expires = now.tv_sec + 100;
rt6.expires = now.tv_sec + 100;
rt6.cb=test_cb;
registration_scheduler.insert_timer_leastloaded(&rt6, now.tv_sec+5, now.tv_sec+50);
@ -1856,7 +1857,7 @@ void DBRegAgent::run_tests() {
gettimeofday(&now, 0);
RegTimer rt7;
rt6.expires = now.tv_sec + 980;
rt6.expires = now.tv_sec + 980;
rt6.cb=test_cb;
registration_scheduler.insert_timer_leastloaded(&rt6, now.tv_sec+9980, now.tv_sec+9990);

@ -28,6 +28,7 @@
#include "ModMysql.h"
#include "log.h"
#include "AmUtils.h"
#include "DBUtils.h"
#include "DSMSession.h"
#include "AmSession.h"
@ -110,6 +111,7 @@ mysqlpp::Connection* getMyDSMSessionConnection(DSMSession* sc_sess) {
sc_sess->SET_STRERROR("No connection to database (not mysqlpp::Connection)");
return NULL;
}
apply_mysql_options(*res, dbConnectTimeoutOption(5), dbReadTimeoutOption(5), dbWriteTimeoutOption(5));
return res;
}
@ -222,8 +224,9 @@ EXEC_ACTION_START(SCMyConnectAction) {
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyDisconnectAction) {
mysqlpp::Connection* conn =
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
@ -241,13 +244,14 @@ EXEC_ACTION_START(SCMyDisconnectAction) {
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyResolveQueryParams) {
sc_sess->var["db.qstr"] =
sc_sess->var["db.qstr"] =
replaceQueryParams(arg, sc_sess, event_params);
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyExecuteAction) {
mysqlpp::Connection* conn =
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
return false;
string qstr = replaceQueryParams(arg, sc_sess, event_params);
@ -267,24 +271,25 @@ EXEC_ACTION_START(SCMyExecuteAction) {
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
ERROR("DB query '%s' failed: '%s'\n",
qstr.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
} EXEC_ACTION_END;
EXEC_ACTION_START(SCMyQueryAction) {
mysqlpp::Connection* conn =
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
if (NULL == conn)
return false;
string qstr = replaceQueryParams(arg, sc_sess, event_params);
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::StoreQueryResult res = query.store();
mysqlpp::StoreQueryResult res = query.store();
if (res) {
// MySQL++ does not allow working with pointers here, so copy construct it
DSMMyStoreQueryResult* m_res = new DSMMyStoreQueryResult(res);
@ -314,15 +319,16 @@ EXEC_ACTION_START(SCMyQueryAction) {
CONST_ACTION_2P(SCMyQueryGetResultAction, ',', true);
EXEC_ACTION_START(SCMyQueryGetResultAction) {
mysqlpp::Connection* conn =
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
if (NULL == conn)
return false;
string qstr = replaceQueryParams(par1, sc_sess, event_params);
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::StoreQueryResult res = query.store();
mysqlpp::StoreQueryResult res = query.store();
if (res) {
unsigned int rowindex_i = 0;
string rowindex = resolveVars(par2, sess, sc_sess, event_params);
@ -456,8 +462,9 @@ EXEC_ACTION_START(SCMyUseResultAction) {
bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event,
map<string,string>* event_params, const string& par1, const string& par2,
bool looped, bool front) {
mysqlpp::Connection* conn =
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
EXEC_ACTION_STOP;
@ -465,20 +472,20 @@ bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType e
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::UseQueryResult res = query.use();
mysqlpp::UseQueryResult res = query.use();
if (res) {
mysqlpp::Row row = res.fetch_row();
if (!row) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("result does not have row");
EXEC_ACTION_STOP;
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("result does not have row");
EXEC_ACTION_STOP;
}
FILE *t_file = tmpfile();
if (NULL == t_file) {
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("tmpfile() failed: "+string(strerror(errno)));
EXEC_ACTION_STOP;
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("tmpfile() failed: "+string(strerror(errno)));
EXEC_ACTION_STOP;
}
fwrite(row.at(0).data(), 1, row.at(0).size(), t_file);
@ -486,9 +493,9 @@ bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType e
DSMDisposableAudioFile* a_file = new DSMDisposableAudioFile();
if (a_file->fpopen(par2, AmAudioFile::Read, t_file)) {
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("fpopen failed!");
EXEC_ACTION_STOP;
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("fpopen failed!");
EXEC_ACTION_STOP;
}
a_file->loop = looped;
@ -496,15 +503,15 @@ bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType e
sc_sess->addToPlaylist(new AmPlaylistItem(a_file, NULL), front);
sc_sess->transferOwnership(a_file);
sc_sess->CLR_ERRNO;
sc_sess->CLR_ERRNO;
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR("query does not have result");
sc_sess->SET_STRERROR("query does not have result");
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
ERROR("DB query '%s' failed: '%s'\n",
qstr.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}
@ -531,40 +538,41 @@ EXEC_ACTION_START(SCMyPlayDBAudioLoopedAction) {
CONST_ACTION_2P(SCMyGetFileFromDBAction, ',', true);
EXEC_ACTION_START(SCMyGetFileFromDBAction) {
mysqlpp::Connection* conn =
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
if (NULL == conn)
return false;
string qstr = replaceQueryParams(par1, sc_sess, event_params);
string fname = resolveVars(par2, sess, sc_sess, event_params);
try {
mysqlpp::Query query = conn->query(qstr.c_str());
mysqlpp::UseQueryResult res = query.use();
mysqlpp::UseQueryResult res = query.use();
if (res) {
mysqlpp::Row row = res.fetch_row();
if (!row) {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("result does not have row");
return false;
sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW);
sc_sess->SET_STRERROR("result does not have row");
return false;
}
FILE *t_file = fopen(fname.c_str(), "wb");
if (NULL == t_file) {
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("fopen() failed for file '"+fname+"': "+string(strerror(errno)));
return false;
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
sc_sess->SET_STRERROR("fopen() failed for file '"+fname+"': "+string(strerror(errno)));
return false;
}
fwrite(row.at(0).data(), 1, row.at(0).size(), t_file);
fclose(t_file);
sc_sess->CLR_ERRNO;
sc_sess->CLR_ERRNO;
} else {
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR("query does not have result");
sc_sess->SET_STRERROR("query does not have result");
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
ERROR("DB query '%s' failed: '%s'\n",
qstr.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
@ -574,9 +582,10 @@ EXEC_ACTION_START(SCMyGetFileFromDBAction) {
CONST_ACTION_2P(SCMyPutFileToDBAction, ',', true);
EXEC_ACTION_START(SCMyPutFileToDBAction) {
mysqlpp::Connection* conn =
mysqlpp::Connection* conn =
getMyDSMSessionConnection(sc_sess);
if (NULL == conn)
if (NULL == conn)
return false;
string qstr = replaceQueryParams(par1, sc_sess, event_params);
@ -628,9 +637,9 @@ EXEC_ACTION_START(SCMyPutFileToDBAction) {
}
} catch (const mysqlpp::Exception& e) {
ERROR("DB query '%s' failed: '%s'\n",
ERROR("DB query '%s' failed: '%s'\n",
par1.c_str(), e.what());
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_ERRNO(DSM_ERRNO_MY_QUERY);
sc_sess->SET_STRERROR(e.what());
sc_sess->var["db.ereason"] = e.what();
}

@ -0,0 +1,137 @@
#pragma once
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include <initializer_list>
#include <memory>
#include <mysql++/mysql++.h>
#include "log.h"
/*
Utilization examples:
With list of arguments:
```
std::initializer_list<DBArgOption> args = {
dbReconnectOption,
dbConnectTimeoutOption(5),
dbReadTimeoutOption(5),
dbWriteTimeoutOption(5),
dbFoundRowsOption
};
apply_mysql_options(conn1, args);
apply_mysql_options(conn2, args);
apply_mysql_options(conn3, args);
```
As a variadic function:
```
apply_mysql_options(conn, dbConnectTimeoutOption(5), dbReadTimeoutOption(5));
```
Variadic function, only setting reconnectionOption:
```
apply_mysql_options(conn, dbReconnectOption);
```
Variadic function, no time set(default: 5) and out of order:
```
apply_mysql_options(conn, dbFoundRowsOption, dbConnectTimeoutOption);
```
Note that ```apply_mysql_options(conn)``` should do nothing.
*/
enum class DbOptionId { Reconnect, ConnectTimeout, ReadTimeout, WriteTimeout, FoundRows };
using Value = std::variant<int, bool, std::string>;
using Kv = std::pair<DbOptionId, Value>;
struct Key
{
DbOptionId id;
template <class T>
auto operator()(T&& v) const {
return Kv{ id, Value{std::forward<T>(v)} };
}
};
inline constexpr int defaultTimeoutSec = 5;
inline constexpr Key dbReconnectOption{DbOptionId::Reconnect};
inline constexpr Key dbConnectTimeoutOption{DbOptionId::ConnectTimeout};
inline constexpr Key dbReadTimeoutOption{DbOptionId::ReadTimeout};
inline constexpr Key dbWriteTimeoutOption{DbOptionId::WriteTimeout};
inline constexpr Key dbFoundRowsOption{DbOptionId::FoundRows};
using DBArgOption = std::variant<Key, Kv>;
template<class T>
concept ArgLike = std::is_same_v<std::decay_t<T>, Key> || std::is_same_v<std::decay_t<T>, Kv>;
template<typename Opt, typename... Args>
inline void set_opt(mysqlpp::Connection& conn, Args&&... args) {
auto p = std::make_unique<Opt>(std::forward<Args>(args)...);
conn.set_option(p.release());
}
// Flags (no value). Example: passing `dbReconnectOption` alone sets reconnect=true.
inline void process_arg(mysqlpp::Connection& conn, const Key& k) {
DbOptionId id = k.id;
switch(id) {
case DbOptionId::Reconnect:
set_opt<mysqlpp::ReconnectOption>(conn, true);
break;
case DbOptionId::FoundRows:
set_opt<mysqlpp::FoundRowsOption>(conn, true);
break;
case DbOptionId::ConnectTimeout:
set_opt<mysqlpp::ConnectTimeoutOption>(conn, defaultTimeoutSec);
break;
case DbOptionId::ReadTimeout:
set_opt<mysqlpp::ReadTimeoutOption>(conn, defaultTimeoutSec);
break;
case DbOptionId::WriteTimeout:
set_opt<mysqlpp::WriteTimeoutOption>(conn, defaultTimeoutSec);
break;
default:
ERROR("Unknown DB id(%d) Option without value.\n", static_cast<int>(id));
break;
}
}
template<class T, class MakeUnique>
inline void set_as(mysqlpp::Connection& conn, const Value& val, MakeUnique make_unique) {
if (const T* p = std::get_if<T>(&val)) {
conn.set_option(make_unique(*p).release());
}
}
// Key/value pairs. Example: dbConnectTimeoutOption(5) sets a 5s connect timeout.
inline void process_arg(mysqlpp::Connection& conn, const Kv& kv) {
const auto& [id, val] = kv;
switch(id) {
case DbOptionId::ConnectTimeout:
set_as<int>(conn, val, [&](int x) { return std::make_unique<mysqlpp::ConnectTimeoutOption>(x); });
break;
case DbOptionId::ReadTimeout:
set_as<int>(conn, val, [&](int x) { return std::make_unique<mysqlpp::ReadTimeoutOption>(x); });
break;
case DbOptionId::WriteTimeout:
set_as<int>(conn, val, [&](int x) { return std::make_unique<mysqlpp::WriteTimeoutOption>(x); });
break;
case DbOptionId::Reconnect:
set_as<bool>(conn, val, [&](bool x) { return std::make_unique<mysqlpp::ReconnectOption>(x); });
break;
case DbOptionId::FoundRows:
set_as<bool>(conn, val, [&](bool x) { return std::make_unique<mysqlpp::FoundRowsOption>(x); });
break;
default:
std::string valueStr;
if (auto p = std::get_if<std::string>(&val)) {
valueStr = *p;
} else if (auto p = std::get_if<int>(&val)) {
valueStr = std::to_string(*p);
} else if (auto p = std::get_if<bool>(&val)) {
valueStr = *p ? "true" : "false";
} else {
valueStr = "<unknown type>";
}
ERROR("Unknown DB id(%d) Option with value (%s).\n", static_cast<int>(id), valueStr.c_str());
break;
}
}
template<ArgLike... Args>
inline void apply_mysql_options(mysqlpp::Connection& conn, Args&&... args) {
(process_arg(conn, std::forward<Args>(args)), ...);
}
inline void apply_mysql_options(mysqlpp::Connection& conn,
std::initializer_list<DBArgOption> args) {
for (const auto& a : args) {
std::visit([&](const auto& x) { process_arg(conn, x); }, a);
}
}
Loading…
Cancel
Save