common/settings.h (1,041 lines of code) (raw):
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0, as
* published by the Free Software Foundation.
*
* This program is designed to work with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms, as
* designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have either included with
* the program or referenced in the documentation.
*
* Without limiting anything contained in the foregoing, this file,
* which is part of Connector/C++, is also subject to the
* Universal FOSS Exception, version 1.0, a copy of which can be found at
* https://oss.oracle.com/licenses/universal-foss-exception.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License, version 2.0, 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 Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MYSQLX_COMMON_SETTINGS_INT_H
#define MYSQLX_COMMON_SETTINGS_INT_H
#include "common.h"
#include "value.h"
#include <mysql/cdk.h>
#include <json_parser.h>
#include <uri_parser.h>
PUSH_SYS_WARNINGS
#include <vector>
#include <bitset>
#include <sstream>
POP_SYS_WARNINGS
namespace mysqlx {
MYSQLX_ABI_BEGIN(2,0)
namespace common {
/*
A class for "transactional" adding new settings to a given
Settings_impl instance.
The primary way of specifying new options is via CDK document containing
key-value pairs with options or option names and their values. A Setter
instance can act as a processor for such CDK document.
*/
class Settings_impl::Setter
: public cdk::JSON::Processor
, public parser::URI_processor
, cdk::JSON::Processor::Any_prc
, cdk::JSON::Processor::Any_prc::Scalar_prc
, cdk::JSON::Processor::Any_prc::List_prc
{
Settings_impl &m_settings;
Settings_impl::Data m_data;
// SessionOption is > 0
// ClientOption is < 0
int m_cur_opt = 0;
void set_comma_separated(int opt, const std::string& val);
/*
Note: Methods below would be best implemented inside Settings_impl::Data
class, but this can break ABI, so we put them here instead.
*/
using iterator = option_list_t::const_reverse_iterator;
iterator find_opt(int opt, iterator start) const;
iterator find_opt(int opt) const
{
return find_opt(opt, m_data.m_options.crbegin());
}
iterator end() const
{
return m_data.m_options.crend();
}
bool has_option(Session_option_impl opt)
{
return end() != find_opt(opt);
}
bool has_option(Client_option_impl opt)
{
return end() != find_opt(-opt);
}
public:
Setter(Settings_impl &settings)
: m_settings(settings)
, m_data(settings.m_data)
{}
void set_client_opts(const Settings_impl &opts)
{
Setter set(*this);
for (auto &opt_val : opts.m_data.m_options)
{
set.add_option(opt_val.first, opt_val.second);
}
set.commit();
}
/*
This method should be called after setting options to actually update
settings in the Settings_impl instance. Settings are updated only if
all consistency checks are passed.
*/
void commit()
{
if (has_option(Session_option_impl::DNS_SRV))
{
if (0 == m_data.m_host_cnt)
{
throw_error("No DNS name specified for SRV lookup");
}
if (1 < m_data.m_host_cnt)
{
throw_error(
"Specifying multiple hostnames with DNS SRV look up is not allowed."
);
}
if (m_data.m_sock)
{
throw_error(
"Using Unix domain sockets with DNS SRV lookup is not allowed."
);
}
if (m_data.m_user_priorities)
{
throw_error(
"Specifying a priority with DNS SRV lookup is not allowed."
);
}
if (has_option(Session_option_impl::PORT))
{
throw_error(
"Specifying a port number with DNS SRV lookup is not allowed."
);
}
}
/*
If more hosts are added to the settings, error if the first host was
defined by PORT only, without explicit HOST setting.
*/
if (
m_data.m_tcpip && m_settings.m_data.m_tcpip
&& 0 == m_settings.m_data.m_host_cnt
)
throw_error("PORT without explicit HOST in multi-host settings");
/*
Check if priority is missing in case some priorities were specified
earlier.
*/
if (m_data.m_user_priorities && (m_host && !m_prio))
throw_error("Expected PRIORITY for a host in multi-host settings");
/*
If all is OK, copy settings collected here to the parent settings
object.
*/
m_settings.m_data = std::move(m_data);
}
// JSON doc processor
void doc_end() override
{
commit();
}
Any_prc* key_val(const string &opt) override
{
/*
Note: This overload is used only when getting options from a
JSON document. Currently only client options can be set that way,
and the only possible top-level client option is 'pooling'.
TODO: Generic infrastructure for handling an alternative way of setting
options using structured documents (current implementation assumes
flat options structure).
*/
if (to_upper(opt) != "POOLING")
{
std::string msg = "Invalid client option: " + opt;
throw_error(msg.c_str());
return nullptr;
}
return key_val(Client_option_impl::POOLING);
}
Any_prc* key_val(int opt)
{
m_cur_opt = opt;
return this;
}
private:
/*
Add or replace option value, report error if option defined twice (except
for options which build the list of hosts).
*/
template <typename T> void add_option(int, const T&);
// State used for option consistency checks.
bool m_host = false;
bool m_port = false;
bool m_socket = false;
bool m_prio = false;
std::set<int> m_opt_set;
int m_prev_option = 0;
/*
Certaion options can be defined multiple times only if m_multi is true.
This is used to handle options that take list of values.
*/
bool m_multi = false;
// Set option value doing all consistency checks.
template <int OPT, typename T>
void set_option(const T &val)
{
if (OPT == Session_option_impl::CONNECT_TIMEOUT)
throw_error("The connection timeout value must be a positive integer (including 0)");
add_option(OPT, val);
}
template <int OPT>
void set_option(const int &val)
{
if (0 > val)
throw_error("Option value can not be a negative number");
set_option<OPT>((unsigned)val);
}
template <int OPT, typename T>
void set_cli_option(const T &val)
{
add_option(OPT, val);
}
template <int OPT>
void set_cli_option(const int &val)
{
if (0 > val)
throw_error("Option value can not be a negative number");
set_option<OPT>((unsigned)val);
}
// Any processor
Scalar_prc* scalar() override
{
return this;
}
// Array values.
List_prc* arr() override
{
switch (m_cur_opt)
{
// Note: allow multiple definitions of these options to store list
// of values, but only if they were not defined before.
case Session_option_impl::TLS_CIPHERSUITES:
m_multi = !m_data.m_tls_ciphers;
m_data.m_tls_ciphers = true;
break;
case Session_option_impl::TLS_VERSIONS:
m_multi = !m_data.m_tls_vers;
m_data.m_tls_vers = true;
break;
case Session_option_impl::COMPRESSION_ALGORITHMS:
m_multi = !m_data.m_compression_algorithms;
m_data.m_compression_algorithms = true;
break;
default:
{
std::stringstream err_msg;
err_msg << "Option " << option_name(m_cur_opt) <<
" does not accept array values";
throw_error(err_msg.str().c_str());
}
return nullptr; // Keep compiler happy
}
// Even if no values given for a list option, we still consider it set to
// an empty list
m_opt_set.insert(m_cur_opt);
return this;
}
virtual void list_end() override
{
m_multi = false;
}
Element_prc* list_el() override
{
return this;
}
// Document values.
Doc_prc* doc() override
{
switch (m_cur_opt)
{
case Client_option_impl::POOLING:
return &m_pool_processor;
case Session_option_impl::CONNECTION_ATTRIBUTES:
return &m_attr_processor;
default:
{
std::stringstream err_msg;
err_msg << "Option " << option_name(m_cur_opt) <<
" does not accept document values";
throw_error(err_msg.str().c_str());
}
}
return nullptr;
}
// Scalar processor
void str(const string &val) override;
void num(uint64_t val) override;
void null() override;
void num(int64_t val) override
{
if (0 > val)
throw_error("Option value can not be a negative number");
num(uint64_t(val));
}
void yesno(bool) override;
// These value types should not be used
void num(float) override
{
assert(false);
}
void num(double) override
{
assert(false);
}
public:
// URI processor
void scheme(const std::string &) override;
void user(const std::string &usr) override;
void password(const std::string &pwd) override;
void schema(const std::string &db) override;
void host(unsigned short priority, const std::string &host) override;
void host(
unsigned short priority,
const std::string &host,
unsigned short port
) override;
void socket(unsigned short priority, const std::string &path) override;
void pipe(unsigned short /*priority*/, const std::string &/*pipe*/) override
{
// should not happen
assert(false);
}
/*
Callbacks for reporting the query component, which is a sequence
of key-value pair. Keys without any value are allowed. Key value
can be a list: "...&key=[v1,..,vN]&...".
*/
void key_val(const std::string &key, const std::string &val) override;
void key_val(const std::string &key) override;
void key_val(const std::string &key, const std::list<std::string>&) override;
static int get_uri_option(const std::string&);
private:
// Processors for processing document option values.
// Connection attributes.
struct Attr_processor
: parser::JSON_parser::Processor
, Any_prc
, Scalar_prc
{
Settings_impl::Data &m_data;
string m_key;
Attr_processor(Settings_impl::Data &data) //ings_impl::Data &data)
: m_data(data)
{}
Any_prc* key_val(const string &key) override
{
if (key.length() == 0)
throw_error("Invalid empty key on connection attributes");
if (key[0] == '_')
throw_error("Connection attribute names cannot start with \"_\".");
m_key = key;
return this;
}
Scalar_prc* scalar() override
{
return this;
}
// Arrays and documents are not valid... throw error
List_prc* arr() override
{
throw_error("Connection attribute can not be an array");
return nullptr;
}
// Report that any value is a document.
Doc_prc* doc() override
{
throw_error("Connection attribute can not be a document");
return nullptr;
}
void null() override
{
m_data.m_connection_attr[m_key];
}
void str(const string &val) override
{
m_data.m_connection_attr[m_key] = val;
}
virtual void num(uint64_t)override
{throw_error("Connection attributes values can't be of integer type");}
virtual void num(int64_t) override
{throw_error("Connection attributes values can't be of integer type");}
virtual void num(float) override
{throw_error("Connection attributes values can't be of integer type");}
virtual void num(double) override
{throw_error("Connection attributes values can't be of integer type");}
virtual void yesno(bool) override
{throw_error("Connection attributes values can't be of boolean type");}
}
m_attr_processor{ m_data };
// Pool settings.
struct Pool_processor
: parser::JSON_parser::Processor
, Any_prc
{
Setter &m_setter;
string m_key;
Pool_processor(Setter &setter) //ings_impl::Data &data)
: m_setter(setter)
{}
Any_prc* key_val(const string &key) override
{
std::string upper_key = to_upper(key);
if (upper_key == "ENABLED")
return this;
else if (upper_key == "MAXSIZE")
return m_setter.key_val(Client_option_impl::POOL_MAX_SIZE);
else if (upper_key == "QUEUETIMEOUT")
return m_setter.key_val(Client_option_impl::POOL_QUEUE_TIMEOUT);
else if (upper_key == "MAXIDLETIME")
return m_setter.key_val(Client_option_impl::POOL_MAX_IDLE_TIME);
std::string msg = "Invalid pooling option: " + key;
throw_error(msg.c_str());
// Quiet compiler warnings
return nullptr;
}
Scalar_prc* scalar() override
{
// 'pooling.enabled' is equivalent to scalar value of POOLING option
return m_setter.key_val(Client_option_impl::POOLING)->scalar();
}
List_prc* arr() override
{
throw_error("Value of 'pooling.enabled' option can be only true or false");
return nullptr;
}
Doc_prc* doc() override
{
throw_error("Value of 'pooling.enabled' option can be only true or false");
return nullptr;
}
}
m_pool_processor{ *this };
};
inline
auto Settings_impl::Setter::find_opt(int opt, iterator start) const
-> iterator
{
return std::find_if(start, m_data.m_options.crend(),
[opt](opt_val_t el) -> bool { return el.first == opt; }
);
}
/*
Logic for handling individual options.
*/
// Options which build a list of hosts.
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::HOST>(
const std::string &val
)
{
if (0 == m_data.m_host_cnt && m_port)
throw_error("PORT without prior host specification in multi-host settings");
/*
In the case of explicit priorities, if a previous host was added, check that
a priority was specified for the previous host.
*/
if (m_data.m_user_priorities && m_host && !m_prio)
throw_error("PRIORITY not set for all hosts in a multi-host settings");
m_host = true;
m_port = false;
m_socket = false;
m_prio = false;
++m_data.m_host_cnt;
m_data.m_tcpip = true;
add_option(Session_option_impl::HOST, val);
}
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::SOCKET>(
#ifdef _WIN32
const std::string&
#else
const std::string &val
#endif
)
{
#ifdef _WIN32
throw_error("SOCKET option not supported on Windows");
#else
/*
In the case of explicit priorities, if a previous host was added, check that
a priority was specified for the previous host.
*/
if (m_data.m_user_priorities && m_host && !m_prio)
throw_error("PRIORITY not set for all hosts in a multi-host settings");
m_host = true;
m_socket = true;
m_prio = false;
m_port = false;
++m_data.m_host_cnt;
m_data.m_sock = true;
add_option(Session_option_impl::SOCKET, val);
#endif
}
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::PORT>(
const unsigned &val
)
{
if (m_port)
throw_error("duplicate PORT value"); // TODO: overwrite instead?
if (0 < m_data.m_host_cnt && (Session_option_impl::HOST != m_prev_option))
throw_error("PORT must follow HOST setting in multi-host settings");
if (m_socket)
throw_error("Invalid PORT setting for socked-based connection");
if (m_prio)
throw_error("PORT should be specified before PRIORITY");
if (val > 65535U)
throw_error("Port value out of range");
m_port = true;
m_data.m_tcpip = true;
add_option(Session_option_impl::PORT, val);
}
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::PRIORITY>(
const unsigned &val
)
{
switch (m_prev_option)
{
case Session_option_impl::HOST:
case Session_option_impl::PORT:
case Session_option_impl::SOCKET:
break;
default:
throw_error("PRIORITY must directly follow host specification");
};
if (m_prio)
throw_error("duplicate PRIORITY value"); // TODO: overwrite instead?
/*
Using PRIORITY implies multi-host settings and then each host must be
defined explicitly.
*/
if (!m_host)
throw_error("PRIORITY without prior host specification");
if (1 < m_data.m_host_cnt && !m_data.m_user_priorities)
throw_error("PRIORITY not set for all hosts in a multi-host settings");
if (val > 100)
throw_error("PRIORITY should be a number between 0 and 100");
m_data.m_user_priorities = true;
m_prio = true;
add_option(Session_option_impl::PRIORITY, val);
}
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::COMPRESSION>(
const unsigned &val
)
{
if (val >= size_t(Compression_mode::LAST))
throw_error("Invalid Compression value");
add_option(Session_option_impl::COMPRESSION, val);
}
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::COMPRESSION>(
const std::string &val
)
{
using std::map;
#define COMPRESSION_MAP(X,N) { #X, Compression_mode::X },
static map< std::string, Compression_mode > compression_map{
COMPRESSION_MODE_LIST(COMPRESSION_MAP)
};
try {
Compression_mode m = compression_map.at(to_upper(val));
if (Compression_mode::LAST == m)
throw std::out_of_range("");
set_option<Session_option_impl::COMPRESSION>(unsigned(m));
return;
}
catch (const std::out_of_range&)
{
std::string msg = "Invalid compression mode: " + val;
throw_error(msg.c_str());
// Quiet compiler warnings
return;
}
}
// SSL options.
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::SSL_MODE>(
const unsigned &val
)
{
if (val >= size_t(SSL_mode::LAST))
throw_error("Invalid SSL_MODE value");
m_data.m_ssl_mode = SSL_mode(val);
#ifndef WITH_SSL
if (SSL_mode::DISABLED != m_ssl_mode)
throw_error("secure connection requested but SSL is not supported")
#endif
add_option(Session_option_impl::SSL_MODE, val);
}
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::SSL_CA>(
const std::string &val
)
{
#ifndef WITH_SSL
throw_error("SSL_CA option specified but SSL is not supported")
#endif
m_data.m_ssl_ca = true;
add_option(Session_option_impl::SSL_CA, val);
}
template <>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::CONNECT_TIMEOUT>(
const uint64_t &timeout
)
{
add_option(Settings_impl::Session_option_impl::CONNECT_TIMEOUT, timeout);
}
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::SSL_MODE>(
const std::string &val
)
{
using std::map;
#define SSL_MODE_MAP(X,N) { #X, SSL_mode::X },
static map< std::string, SSL_mode > option_map{
SSL_MODE_LIST(SSL_MODE_MAP)
};
try {
SSL_mode opt = option_map.at(to_upper(val));
if (SSL_mode::LAST == opt)
throw std::out_of_range("");
set_option<Session_option_impl::SSL_MODE>(unsigned(opt));
return;
}
catch (const std::out_of_range&)
{
std::string msg = "Invalid ssl mode value: " + val;
throw_error(msg.c_str());
// Quiet compiler warnings
return;
}
}
// Authentication options.
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::AUTH>(
const unsigned &val
)
{
if (val >= size_t(Auth_method::LAST))
throw_error("Invalid auth method");
add_option(Session_option_impl::AUTH, val);
}
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::AUTH>(
const std::string &val
)
{
using std::map;
#define AUTH_MAP(X,N) { #X, Auth_method::X },
static map< std::string, Auth_method > auth_map{
AUTH_METHOD_LIST(AUTH_MAP)
};
try {
Auth_method m = auth_map.at(to_upper(val));
if (Auth_method::LAST == m)
throw std::out_of_range("");
set_option<Session_option_impl::AUTH>(unsigned(m));
return;
}
catch (const std::out_of_range&)
{
std::string msg = "Invalid auth method: " + val;
throw_error(msg.c_str());
// Quiet compiler warnings
return;
}
}
// Connection attributes.
template<>
inline void
Settings_impl::Setter::set_option<
Settings_impl::Session_option_impl::CONNECTION_ATTRIBUTES>(const bool& val)
{
if (val)
m_data.init_connection_attr();
else
m_data.clear_connection_attr();
}
template<>
inline void
Settings_impl::Setter::set_option<
Settings_impl::Session_option_impl::CONNECTION_ATTRIBUTES>(const std::string &val)
{
struct processor
: parser::JSON_parser::Processor
, Any_prc
, Scalar_prc
{
Settings_impl::Data &m_data;
string m_key;
processor(Settings_impl::Data &data)
: m_data(data)
{}
Any_prc* key_val(const string &key) override
{
if (key.length() == 0)
throw_error("Invalid empty key on connection attributes");
if (key[0] == '_')
throw_error("Connection attribute names cannot start with \"_\".");
m_key = key;
return this;
}
Scalar_prc* scalar() override
{
return this;
}
// Arrays and documents are not valid... throw error
List_prc* arr() override
{
throw_error("Connection attribute can not be an array");
return nullptr;
}
// Report that any value is a document.
Doc_prc* doc() override
{
throw_error("Connection attribute can not be a document");
return nullptr;
}
void null() override
{
m_data.m_connection_attr[m_key];
}
void str(const string &val) override
{
m_data.m_connection_attr[m_key] = val;
}
virtual void num(uint64_t)override
{throw_error("Connection attributes values can't be of integer type");}
virtual void num(int64_t) override
{throw_error("Connection attributes values can't be of integer type");}
virtual void num(float) override
{throw_error("Connection attributes values can't be of integer type");}
virtual void num(double) override
{throw_error("Connection attributes values can't be of integer type");}
virtual void yesno(bool) override
{throw_error("Connection attributes values can't be of boolean type");}
};
parser::JSON_parser parser(val);
processor prc(m_data);
parser.process(prc);
}
// TODO: support std::string for PWD and other options that are ascii only?
template<>
inline void
Settings_impl::Setter::set_option<Settings_impl::Session_option_impl::URI>(
const std::string &val
)
{
parser::URI_parser parser(val);
parser.process(*this);
}
template<>
inline void
Settings_impl::Setter::set_cli_option<
Settings_impl::Client_option_impl::POOL_MAX_SIZE
>(const uint64_t &val)
{
if (val == 0)
throw_error("Max pool size has to be greater than 0");
add_option(Settings_impl::Client_option_impl::POOL_MAX_SIZE, val);
}
inline void
Settings_impl::Setter::set_comma_separated(int opt, const std::string& val)
{
std::string lval = "";
for (auto it = val.begin(); it != val.end(); it++)
{
const char c = *it;
if (isspace(static_cast<unsigned char>(c)) ||
c == ',')
{
if (lval.length())
{
add_option(opt, lval);
lval = "";
// If first add_option() was OK, then we disable duplicate checks
// to allow adding remaining values of the option in the
// following iterations
m_multi = true; // _state = LIST_PROCESS;
}
continue;
}
lval += c;
}
if (lval.length())
add_option(opt, lval); // Add the last value
m_multi = false; // _state = LIST_END;
}
template<>
inline void
Settings_impl::Setter::set_option<
Settings_impl::Session_option_impl::TLS_CIPHERSUITES
>(const std::string &val)
{
m_data.m_tls_ciphers = true; // record that the option was set
// If in multi mode, the value is a single list element, otherwise
// the value can be a comma separated list
if (!m_multi)
set_comma_separated((int)Settings_impl::Session_option_impl::TLS_CIPHERSUITES, val);
else
add_option((int)Settings_impl::Session_option_impl::TLS_CIPHERSUITES, val);
}
template<>
inline void
Settings_impl::Setter::set_option<
Settings_impl::Session_option_impl::TLS_VERSIONS
>(const std::string &val)
{
m_data.m_tls_vers = true; // record that the option was set
// If in multi mode, the value is a single list element, otherwise
// the value can be a comma separated list
if (!m_multi)
set_comma_separated((int)Settings_impl::Session_option_impl::TLS_VERSIONS, val);
else
add_option((int)Settings_impl::Session_option_impl::TLS_VERSIONS, val);
}
template<>
inline void
Settings_impl::Setter::set_option<
Settings_impl::Session_option_impl::COMPRESSION_ALGORITHMS
>(const std::string &val)
{
m_data.m_compression_algorithms = true; // record that the option was set
// If in multi mode, the value is a single list element, otherwise
// the value can be a comma separated list
if (!m_multi)
set_comma_separated((int)Settings_impl::Session_option_impl::COMPRESSION_ALGORITHMS, val);
else
add_option((int)Settings_impl::Session_option_impl::COMPRESSION_ALGORITHMS, val);
}
// Generic add_option() method.
template <typename T>
inline
void Settings_impl::Setter::add_option(int opt, const T &val)
{
auto &options = m_data.m_options;
m_prev_option = opt;
switch (opt)
{
case Session_option_impl::HOST:
case Session_option_impl::SOCKET:
case Session_option_impl::PORT:
case Session_option_impl::PRIORITY:
options.emplace_back(opt, val);
return;
case Session_option_impl::TLS_CIPHERSUITES:
case Session_option_impl::TLS_VERSIONS:
case Session_option_impl::COMPRESSION_ALGORITHMS:
if (m_multi)
{
options.emplace_back(opt, val);
m_opt_set.insert(opt); // needed for double check when m_multi is false
return;
}
}
auto it = options.begin();
for (; it != options.end(); ++it)
{
if (it->first == opt)
{
it->second = val;
break;
}
}
if (it == options.end())
{
options.emplace_back(opt, val);
}
}
// Value processor
inline
void Settings_impl::Setter::str(const string &val)
{
// TODO: avoid utf8 conversions
std::string utf8_val(val);
auto to_number = [&]() -> uint64_t
{
std::size_t pos;
long long x = std::stoll(utf8_val, &pos);
if (x < 0)
throw_error("Option ... accepts only non-negative values");
if (pos != val.length())
throw_error("Option ... accepts only integer values");
return x;
};
#define SET_OPTION_STR_str(X,N) \
case Session_option_impl::X: return set_option<Session_option_impl::X,std::string>(utf8_val);
#define SET_OPTION_STR_any(X,N) SET_OPTION_STR_str(X,N)
#define SET_OPTION_STR_bool(X,N) SET_OPTION_STR_num(X,N)
#define SET_OPTION_STR_num(X,N) \
case Session_option_impl::X: \
try \
{ \
return set_option<Session_option_impl::X,uint64_t>(to_number()); \
} \
catch (const std::invalid_argument&) \
{ \
throw_error("Can not convert to integer value"); \
}
#define SET_OPTION_STR_bool(X,N) SET_OPTION_STR_num(X,N)
switch (m_cur_opt)
{
SESSION_OPTION_LIST(SET_OPTION_STR)
default:
throw_error("Option ... could not be processed.");
}
}
inline
void Settings_impl::Setter::num(uint64_t val)
{
#define SET_OPTION_NUM_num(X,N) \
case Session_option_impl::X: return set_option<Session_option_impl::X,unsigned>((unsigned)val);
#define SET_OPTION_NUM_any(X,N) SET_OPTION_NUM_num(X,N)
#define SET_OPTION_NUM_bool(X,N) SET_OPTION_NUM_num(X,N)
#define SET_OPTION_NUM_str(X,N)
#define SET_CLI_OPTION_NUM_num(X,N) \
case Client_option_impl::X: return set_cli_option<Client_option_impl::X,uint64_t>(val);
#define SET_CLI_OPTION_NUM_bool(X,N) SET_CLI_OPTION_NUM_num(X,N)
#define SET_CLI_OPTION_NUM_any(X,N) SET_CLI_OPTION_NUM_num(X,N)
#define SET_CLI_OPTION_NUM_str(X,N)
/*
This cannot be processed inside switch because the numeric
values are converted to unsigned int. For timeout uint64_t is
required
*/
if (m_cur_opt == Session_option_impl::CONNECT_TIMEOUT)
return set_option<Session_option_impl::CONNECT_TIMEOUT>(val);
if (m_cur_opt == Session_option_impl::CONNECT_TIMEOUT)
return set_option<Session_option_impl::CONNECT_TIMEOUT>(val);
if (m_cur_opt < 0 && !check_num_limits<int64_t>(val))
throw_error("Option ... value too big");
switch (m_cur_opt)
{
SESSION_OPTION_LIST(SET_OPTION_NUM)
CLIENT_OPTION_LIST(SET_CLI_OPTION_NUM)
default:break;
}
throw_error("Option ... does not accept numeric values.");
}
inline
void Settings_impl::Setter::null()
{
switch (m_cur_opt)
{
case Session_option_impl::HOST:
case Session_option_impl::PORT:
case Session_option_impl::PRIORITY:
case Session_option_impl::USER:
throw_error("Option ... can not be unset");
break;
case Session_option_impl::COMPRESSION_ALGORITHMS:
//It has compression algorithms, so don't use default
m_data.m_compression_algorithms = true;
case Session_option_impl::LAST:
break;
default:
m_data.erase(m_cur_opt);
}
}
inline
void Settings_impl::Setter::yesno(bool val)
{
#define SET_OPTION_BOOL_bool(X,N) \
case Session_option_impl::X: return set_option<Session_option_impl::X, bool>(val);
#define SET_OPTION_BOOL_any(X,N) \
case Session_option_impl::X: break;
#define SET_OPTION_BOOL_num(X,N) SET_OPTION_BOOL_any(X,N)
#define SET_OPTION_BOOL_str(X,N) SET_OPTION_BOOL_any(X,N)
#define SET_CLI_OPTION_BOOL_bool(X,N) \
case Client_option_impl::X: return set_option<Client_option_impl::X, bool>(val);
#define SET_CLI_OPTION_BOOL_any(X,N) \
case Client_option_impl::X: break;
#define SET_CLI_OPTION_BOOL_num(X,N) SET_CLI_OPTION_BOOL_any(X,N)
#define SET_CLI_OPTION_BOOL_str(X,N) SET_CLI_OPTION_BOOL_any(X,N)
switch (m_cur_opt)
{
SESSION_OPTION_LIST(SET_OPTION_BOOL)
CLIENT_OPTION_LIST(SET_CLI_OPTION_BOOL)
default:
break;
}
/*
Special handling of CONNECTION_ATTRIBUTES option which is declared
as string option, but it can be also set to a bool value.
*/
switch (m_cur_opt)
{
SET_OPTION_BOOL_bool(CONNECTION_ATTRIBUTES, val)
default:
break;
}
throw_error("Option ... can not be bool");
}
// URI processor
inline
void Settings_impl::Setter::scheme(const std::string &_scheme)
{
if(_scheme == "mysqlx+srv")
set_option<Session_option_impl::DNS_SRV>(true);
}
inline
void Settings_impl::Setter::user(const std::string &usr)
{
set_option<Session_option_impl::USER>(usr);
}
inline
void Settings_impl::Setter::password(const std::string &pwd)
{
set_option<Session_option_impl::PWD>(pwd);
}
inline
void Settings_impl::Setter::schema(const std::string &db)
{
set_option<Session_option_impl::DB>(db);
}
inline
void Settings_impl::Setter::host(
unsigned short priority, const std::string &host
)
{
set_option<Session_option_impl::HOST>(host);
if (0 < priority)
set_option<Session_option_impl::PRIORITY>(priority - 1);
}
inline
void Settings_impl::Setter::host(
unsigned short priority,
const std::string &host,
unsigned short port
)
{
set_option<Session_option_impl::HOST>(host);
set_option<Session_option_impl::PORT>(port);
if (0 < priority)
set_option<Session_option_impl::PRIORITY>(priority - 1);
}
inline
void Settings_impl::Setter::socket(unsigned short priority, const std::string &path)
{
set_option<Session_option_impl::SOCKET>(path);
if (0 < priority)
set_option<Session_option_impl::PRIORITY>(priority - 1);
}
inline
void Settings_impl::Setter::key_val(const std::string &key, const std::string &val)
{
try {
auto option = get_uri_option(key);
switch(option)
{
case Settings_impl::Session_option_impl::CONNECTION_ATTRIBUTES:
{
std::string tmp = to_lower(val);
if (tmp == "false")
{
m_data.m_connection_attr.clear();
}
else if (tmp == "true")
{
m_data.init_connection_attr();
}
else
{
throw_error("The value of a \"session-connect-attribute\" must be "
"either a Boolean or a list of key-value pairs.");
}
}
break;
default:
key_val(option)->scalar()->str(val);
}
}
catch (const std::out_of_range&)
{
throw_error("Invalid URI option ...");
}
}
inline
void Settings_impl::Setter::key_val(const std::string &key)
{
try {
switch(get_uri_option(key))
{
case Session_option_impl::CONNECTION_ATTRIBUTES:
m_data.init_connection_attr();
return;
default:
throw_error("Option ... requires a value");
}
}
catch (const std::out_of_range&)
{
throw_error("invalid URI option ...");
}
}
inline
void Settings_impl::Setter::key_val(const std::string &key,
const std::list<std::string> &list)
{
try {
int option = get_uri_option(key);
switch(option)
{
case Settings_impl::Session_option_impl::CONNECTION_ATTRIBUTES:
for(auto el : list)
{
if (el.empty())
continue;
size_t eq = el.find("=");
std::string attr = el.substr(0,eq);
if (attr[0]== '_')
throw_error("Connection attribute names cannot start with \"_\".");
auto &attr_pos = m_data.m_connection_attr[attr];
if (eq != std::string::npos)
attr_pos = el.substr(eq+1);
}
break;
case Settings_impl::Session_option_impl::TLS_CIPHERSUITES:
case Settings_impl::Session_option_impl::TLS_VERSIONS:
case Settings_impl::Session_option_impl::COMPRESSION_ALGORITHMS:
{
auto *prc = key_val(option)->arr();
if (!prc)
break;
prc->list_begin();
for (auto el : list)
{
if (el.empty())
continue;
safe_prc(prc->list_el())->scalar()->str(el);
}
prc->list_end();
}
break;
default:
std::stringstream err;
err << "Option " << key << " does not accept a list value";
throw_error(err.str().c_str());
break;
}
}
catch (const std::out_of_range&)
{
throw_error("Invalid URI option ...");
}
}
inline
int
Settings_impl::Setter::get_uri_option(const std::string &name)
{
using std::map;
#define URI_OPT_MAP(X,Y) { X, Y },
static map< std::string, int > uri_map{
URI_OPTION_LIST(URI_OPT_MAP)
};
int opt = uri_map.at(to_lower(name));
assert(Session_option_impl::LAST != opt);
return opt;
}
} // common
MYSQLX_ABI_END(2,0)
} // mysqlx namespace
#endif