Skip to content

Commit

Permalink
feat: add passing client flags into options
Browse files Browse the repository at this point in the history
Co-authored-by: Peter Opatril <[email protected]>
  • Loading branch information
PredatorCZ and OpatrilPeter committed Mar 9, 2022
1 parent 57cf765 commit a205d98
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 31 deletions.
116 changes: 88 additions & 28 deletions include/superior_mysqlpp/connection_def.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace SuperiorMySqlpp
{
class Query;

using ClientFlags = LowLevel::DBDriver::ClientFlags;
using ConnectionOptions = LowLevel::DBDriver::DriverOptions;


Expand All @@ -38,6 +39,66 @@ namespace SuperiorMySqlpp
sslConfig.trustedCertificateDirPath, sslConfig.allowableCiphers);
}

/**
* Parses conection options.
* The option tuples are not stored in the structure and are instead
* directly passed into the callback
*/
template <typename Callable>
class OptionParser
{
Callable&& onOption;

template <bool postConnect, typename TP1, typename TP2>
void option(std::integral_constant<bool, postConnect>, const std::tuple<TP1, TP2>& input)
{
onOption(std::get<0>(input), std::get<1>(input));
}

template <bool postConnect, class TP1, class TP2>
void option(std::integral_constant<bool, postConnect>, const std::pair<TP1, TP2>& input)
{
onOption(std::get<0>(input), std::get<1>(input));
}

template <bool postConnect>
void option(std::integral_constant<bool, postConnect>, ClientFlags flag)
{
static_assert(!postConnect, "Client flags may only be set in Connection constructor.");
clientFlags.flags |= flag.flags;
}

template<bool postConnect, typename... OptionTuples, std::size_t... I>
void apply(std::integral_constant<bool, postConnect>, const std::tuple<OptionTuples...>& mainTuple, std::index_sequence<I...>)
{
// Prevent unused variable error if mainTuple is empty
static_cast<void>(mainTuple);
// Wrapped in initializer list to enforce ordering
static_cast<void>(std::initializer_list<int>{(option(std::integral_constant<bool, postConnect>{}, std::get<I>(mainTuple)),0)...});
}

public:
ClientFlags clientFlags{};

template<bool postConnect, typename... OptionTuples>
OptionParser(std::integral_constant<bool, postConnect>, const std::tuple<OptionTuples...>& optTuples, Callable&& onOption)
: onOption{std::forward<Callable>(onOption)}
{
apply(std::integral_constant<bool, postConnect>{}, optTuples, std::index_sequence_for<OptionTuples...>{});
}
};

/**
* Helper because we can't autodeduce template params on OptionParser ctor.
*
* When postConnect flag is set to true, option setting happens after connection was opened
* and settings that cannot be changed at that time aren't allowed.
*/
template<bool postConnect = false, typename... OptionTuples, typename Callable>
static OptionParser<Callable> makeOptionParser(const std::tuple<OptionTuples...>& optTuples, Callable&& onOption) {
return {std::integral_constant<bool, postConnect>{}, optTuples, std::forward<Callable>(onOption)};
}

public:
template<typename... OptionTuples>
Connection(const std::string& database, const std::string& user, const std::string& password="",
Expand All @@ -46,8 +107,10 @@ namespace SuperiorMySqlpp
Loggers::SharedPointer_t loggerPtr=DefaultLogger::getLoggerPtr())
: driver{std::move(loggerPtr)}
{
setOptions(std::move(optionTuples));
driver.connect(host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, nullptr);
auto parser = makeOptionParser(std::move(optionTuples), [&](auto key, auto value) {
this->setOption(key, value);
});
driver.connect(host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, nullptr, parser.clientFlags);
}

template<typename... OptionTuples>
Expand All @@ -57,9 +120,11 @@ namespace SuperiorMySqlpp
Loggers::SharedPointer_t loggerPtr=DefaultLogger::getLoggerPtr())
: driver{std::move(loggerPtr)}
{
setOptions(std::move(optionTuples));
auto parser = makeOptionParser(std::move(optionTuples), [&](auto key, auto value) {
this->setOption(key, value);
});
setSslConfiguration(sslConfig);
driver.connect(host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, nullptr);
driver.connect(host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, nullptr, parser.clientFlags);
}

template<typename... OptionTuples>
Expand All @@ -68,8 +133,10 @@ namespace SuperiorMySqlpp
Loggers::SharedPointer_t loggerPtr=DefaultLogger::getLoggerPtr())
: driver{std::move(loggerPtr)}
{
setOptions(std::move(optionTuples));
driver.connect(nullptr, user.c_str(), password.c_str(), database.c_str(), 0, socketPath.c_str());
auto parser = makeOptionParser(std::move(optionTuples), [&](auto key, auto value) {
this->setOption(key, value);
});
driver.connect(nullptr, user.c_str(), password.c_str(), database.c_str(), 0, socketPath.c_str(), parser.clientFlags);
}

template<typename... OptionTuples>
Expand All @@ -79,9 +146,11 @@ namespace SuperiorMySqlpp
Loggers::SharedPointer_t loggerPtr=DefaultLogger::getLoggerPtr())
: driver{std::move(loggerPtr)}
{
setOptions(std::move(optionTuples));
auto parser = makeOptionParser(std::move(optionTuples), [&](auto key, auto value) {
this->setOption(key, value);
});
setSslConfiguration(sslConfig);
driver.connect(nullptr, user.c_str(), password.c_str(), database.c_str(), 0, socketPath.c_str());
driver.connect(nullptr, user.c_str(), password.c_str(), database.c_str(), 0, socketPath.c_str(), parser.clientFlags);
}

template<typename... OptionTuples>
Expand All @@ -90,7 +159,9 @@ namespace SuperiorMySqlpp
Loggers::SharedPointer_t loggerPtr=DefaultLogger::getLoggerPtr())
: driver{std::move(loggerPtr)}
{
setOptions(std::move(optionTuples));
auto parser = makeOptionParser(std::move(optionTuples), [&](auto key, auto value) {
this->setOption(key, value);
});

if (config.sslConfig)
{
Expand All @@ -99,11 +170,11 @@ namespace SuperiorMySqlpp

if (config.usingSocket)
{
driver.connect(nullptr, config.user.c_str(), config.password.c_str(), config.database.c_str(), 0, config.target.c_str());
driver.connect(nullptr, config.user.c_str(), config.password.c_str(), config.database.c_str(), 0, config.target.c_str(), parser.clientFlags);
}
else
{
driver.connect(config.target.c_str(), config.user.c_str(), config.password.c_str(), config.database.c_str(), config.port, nullptr);
driver.connect(config.target.c_str(), config.user.c_str(), config.password.c_str(), config.database.c_str(), config.port, nullptr, parser.clientFlags);
}
}

Expand Down Expand Up @@ -268,25 +339,14 @@ namespace SuperiorMySqlpp
driver.setDriverOption(option, argumentPtr);
}

template<typename... OptionTuples, std::size_t... I>
void detail_setOptions(std::tuple<OptionTuples...> mainTuple, std::index_sequence<I...>)
{
static_cast<void>(mainTuple); // this prevents unused variable error if mainTuple is empty
/*
* This magic is doing for each argument to call setOption.
* Order of evaluation is guaranteed by the standard.
*/
using IntArray = int[];
static_cast<void>(IntArray{(setOption(
std::get<0>(std::get<I>(mainTuple)),
std::get<1>(std::get<I>(mainTuple))
), 0)..., 0});
}

template<typename... OptionTuples>
void setOptions(std::tuple<OptionTuples...> mainTuple)
void setOptions(std::tuple<OptionTuples...> optionTuples)
{
detail_setOptions(std::move(mainTuple), std::index_sequence_for<OptionTuples...>{});
auto parser = makeOptionParser<false>(std::move(optionTuples), [&](auto key, auto value) {
this->setOption(key, value);
});
// Used for sideeffect
(void)parser;
}


Expand Down
18 changes: 16 additions & 2 deletions include/superior_mysqlpp/low_level/dbdriver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,19 @@ namespace SuperiorMySqlpp { namespace LowLevel
return mysql_character_set_name(getMysqlPtr());
}

/**
* Describes client flag options of mysql_real_connect.
* See https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect.html
*/
struct ClientFlags {
unsigned long flags = 0;

// Implicit default ctor can't be used as we're constructing this
// in default argument of method of encapsulating class
ClientFlags() : flags{} {}
ClientFlags(unsigned long flags) : flags{flags} {}
};

/**
* Connect (or reconnect) to MySQL server.
* @see https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html
Expand All @@ -421,7 +434,8 @@ namespace SuperiorMySqlpp { namespace LowLevel
const char* password,
const char* database,
unsigned int port,
const char* socketName)
const char* socketName,
ClientFlags clientFlags = ClientFlags{})
{
using namespace std::string_literals;

Expand All @@ -433,7 +447,7 @@ namespace SuperiorMySqlpp { namespace LowLevel
id = getGlobalIdRef().fetch_add(1);

getLogger()->logMySqlConnecting(id, host, user, database, port, socketName);
if (mysql_real_connect(getMysqlPtr(), host, user, password, database, port, socketName, CLIENT_MULTI_STATEMENTS) == nullptr)
if (mysql_real_connect(getMysqlPtr(), host, user, password, database, port, socketName, clientFlags.flags | CLIENT_MULTI_STATEMENTS) == nullptr)
{
std::stringstream message{};
message << "Failed to connect to MySQL. (Host: ";
Expand Down
26 changes: 25 additions & 1 deletion tests/db_access/connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ go_bandit([](){
});


it("will can have pre-connect arguments", [&](){
it("can have pre-connect arguments", [&](){
auto& s = getSettingsRef();
unsigned int timeout = 1;
bool reconnect = true;
Expand Down Expand Up @@ -102,6 +102,30 @@ go_bandit([](){
std::make_tuple(SuperiorMySqlpp::ConnectionOptions::reconnect, &reconnect)
)
};

Connection connectionClientFlags1{s.database, s.user, s.password, s.host, s.port,
std::forward_as_tuple(
std::make_tuple(SuperiorMySqlpp::ConnectionOptions::connectTimeout, &timeout),
std::make_tuple(SuperiorMySqlpp::ConnectionOptions::readTimeout, &timeout),
std::make_tuple(SuperiorMySqlpp::ConnectionOptions::writeTimeout, &timeout),
std::make_tuple(SuperiorMySqlpp::ConnectionOptions::reconnect, &reconnect),
SuperiorMySqlpp::ClientFlags{CLIENT_FOUND_ROWS}
)
};

Connection connectionClientFlags2{s.database, s.user, s.password, s.host, s.port,
std::forward_as_tuple(
SuperiorMySqlpp::ClientFlags{CLIENT_FOUND_ROWS},
SuperiorMySqlpp::ClientFlags{CLIENT_COMPRESS}
)
};

Connection connectionClientFlags3{s.database, s.user, s.password, s.host, s.port,
std::forward_as_tuple(
SuperiorMySqlpp::ClientFlags{CLIENT_FOUND_ROWS|CLIENT_COMPRESS}
)
};

});

it("can be moved between threads", [&](){
Expand Down

0 comments on commit a205d98

Please sign in to comment.