17#include <boost/lexical_cast.hpp>
23#include <unordered_map>
34int MySqlHolder::atexit_ = [] {
35 return atexit([] { mysql_library_end(); });
42 : conn_(conn), committed_(false) {
65 const char* host =
"localhost";
74 unsigned int port = 0;
76 setIntParameterValue(
"port", 0, numeric_limits<uint16_t>::max(), port);
78 }
catch (
const std::exception& ex) {
82 const char* user = NULL;
91 const char* password = NULL;
95 password = spassword.c_str();
100 const char* name = NULL;
104 name = sname.c_str();
111 unsigned int read_timeout = 0;
112 unsigned int write_timeout = 0;
117 setIntParameterValue(
"connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
121 setIntParameterValue(
"read-timeout", 0, numeric_limits<int>::max(), read_timeout);
122 setIntParameterValue(
"write-timeout", 0, numeric_limits<int>::max(), write_timeout);
124 }
catch (
const std::exception& ex) {
128 const char* ca_file(0);
129 const char* ca_dir(0);
135 ca_dir = sca.c_str();
137 ca_file = sca.c_str();
143 const char* cert_file(0);
148 cert_file = scert.c_str();
153 const char* key_file(0);
158 key_file = skey.c_str();
163 const char* cipher_list(0);
168 cipher_list = scipher.c_str();
176#ifdef HAS_MYSQL_OPT_RECONNECT
183 result = mysql_options(
mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
191 const char *wait_time =
"SET SESSION wait_timeout = 30 * 86400";
192 result = mysql_options(
mysql_, MYSQL_INIT_COMMAND, wait_time);
202 const char *sql_mode =
"SET SESSION sql_mode ='STRICT_ALL_TABLES'";
203 result = mysql_options(
mysql_, MYSQL_INIT_COMMAND, sql_mode);
211 result = mysql_options(
mysql_, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
219 if (read_timeout > 0) {
220 result = mysql_options(
mysql_, MYSQL_OPT_READ_TIMEOUT, &read_timeout);
229 if (write_timeout > 0) {
230 result = mysql_options(
mysql_, MYSQL_OPT_WRITE_TIMEOUT, &write_timeout);
240 result = mysql_options(
mysql_, MYSQL_OPT_SSL_KEY, key_file);
245 result = mysql_options(
mysql_, MYSQL_OPT_SSL_CERT, cert_file);
250 result = mysql_options(
mysql_, MYSQL_OPT_SSL_CA, ca_file);
255 result = mysql_options(
mysql_, MYSQL_OPT_SSL_CAPATH, ca_dir);
260 result = mysql_options(
mysql_, MYSQL_OPT_SSL_CIPHER, cipher_list);
276 MYSQL* status = mysql_real_connect(
mysql_, host, user, password, name,
277 port, NULL, CLIENT_FOUND_ROWS);
279 std::string error_message = mysql_error(
mysql_);
286 std::ostringstream s;
288 s <<
" (scheduling retry " << rec->retryIndex() + 1 <<
" of " << rec->maxRetries() <<
" in " << rec->retryInterval() <<
" milliseconds)";
290 error_message += s.str();
306 if (autocommit_result != 0) {
319std::pair<uint32_t, uint32_t>
323 const string& timer_name) {
327 if (!timer_name.empty()) {
335 MYSQL_STMT *stmt = mysql_stmt_init(conn.
mysql_);
338 "statement structure, reason: " << mysql_error(conn.
mysql_));
344 const char* version_sql =
"SELECT version, minor FROM schema_version";
345 int status = mysql_stmt_prepare(stmt, version_sql, strlen(version_sql));
348 << version_sql <<
">, reason: "
349 << mysql_error(conn.
mysql_));
355 << version_sql <<
">, reason: "
356 << mysql_errno(conn.
mysql_));
361 memset(bind, 0,
sizeof(bind));
364 bind[0].buffer_type = MYSQL_TYPE_LONG;
365 bind[0].is_unsigned = 1;
367 bind[0].buffer_length =
sizeof(
version);
370 bind[1].buffer_type = MYSQL_TYPE_LONG;
371 bind[1].is_unsigned = 1;
372 bind[1].buffer = &minor;
373 bind[1].buffer_length =
sizeof(minor);
375 if (mysql_stmt_bind_result(stmt, bind)) {
377 << version_sql <<
">, reason: "
378 << mysql_errno(conn.
mysql_));
382 if (mysql_stmt_fetch(stmt)) {
384 << version_sql <<
">, reason: "
385 << mysql_errno(conn.
mysql_));
389 mysql_stmt_close(stmt);
390 return (std::make_pair(
version, minor));
392 }
catch (
const std::exception&) {
394 mysql_stmt_close(stmt);
404 const string& timer_name) {
406 bool const retry(parameters.count(
"retry-on-startup") &&
407 parameters.at(
"retry-on-startup") ==
"true");
410 pair<uint32_t, uint32_t> schema_version;
412 schema_version =
getVersion(parameters, ac, cb, retry ? timer_name :
string());
417 }
catch (exception
const& exception) {
433 schema_version =
getVersion(parameters, ac, cb, retry ? timer_name :
string());
439 if (schema_version != expected_version) {
441 << expected_version.first <<
"." << expected_version.second
442 <<
", found version: " << schema_version.first <<
"."
443 << schema_version.second);
449 if (parameters.count(
"readonly") && parameters.at(
"readonly") ==
"true") {
464 kea_admin_parameters.insert(kea_admin_parameters.begin(),
"db-init");
470 pid_t
const pid(kea_admin.
spawn());
475 if (exit_code != 0) {
482 vector<string> result{
"mysql"};
483 for (
auto const& p : params) {
484 string const& keyword(p.first);
485 string const& value(p.second);
488 if (keyword ==
"user" ||
489 keyword ==
"password" ||
493 result.push_back(
"--" + keyword);
494 result.push_back(value);
501 static unordered_map<string, string> conversions{
502 {
"connect-timeout",
"connect_timeout"},
503 {
"cipher-list",
"ssl-cipher"},
504 {
"cert-file",
"ssl-cert"},
505 {
"key-file",
"ssl-key"},
506 {
"trust-anchor",
"ssl-ca"},
510 if (conversions.count(keyword)) {
511 result.push_back(
"--extra");
512 result.push_back(
"--" + conversions.at(keyword) +
" " + value);
529 if ((index >= statements_.size()) || (statements_[index] != NULL)) {
531 static_cast<int>(index) <<
") or indexed prepared " <<
532 "statement is not null");
537 statements_[index] = mysql_stmt_init(
mysql_);
538 if (statements_[index] == NULL) {
540 "statement structure, reason: " << mysql_error(
mysql_));
543 int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
546 text <<
">, reason: " << mysql_error(
mysql_));
555 tagged_statement != end_statement; ++tagged_statement) {
556 if (tagged_statement->index >= statements_.size()) {
557 statements_.resize(tagged_statement->index + 1, NULL);
562 tagged_statement->text);
571 for (
size_t i = 0; i < statements_.size(); ++i) {
572 if (statements_[i] != NULL) {
573 (void) mysql_stmt_close(statements_[i]);
574 statements_[i] = NULL;
593 MYSQL_TIME& output_time) {
599 const uint32_t valid_lifetime,
600 MYSQL_TIME& expire) {
606 uint32_t valid_lifetime, time_t& cltt) {
621 int status = mysql_query(
mysql_,
"START TRANSACTION");
624 "reason: " << mysql_error(
mysql_));
645 if (mysql_commit(
mysql_) != 0) {
663 if (mysql_rollback(
mysql_) != 0) {
671MySqlConnection::setIntParameterValue(
const std::string& name, int64_t min, int64_t max, T& value) {
678 if (svalue.empty()) {
683 auto parsed_value = boost::lexical_cast<T>(svalue);
685 if ((parsed_value < min) || (parsed_value > max)) {
689 value = parsed_value;
696 svalue <<
") must be an integer between "
697 << min <<
" and " << max);
int version()
returns Kea hooks version.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
A generic exception that is thrown when an unexpected error condition occurs.
Utility class for spawning new processes.
int getExitStatus(const pid_t pid) const
Returns exit status of the process.
bool isRunning(const pid_t pid) const
Checks if the process is still running.
std::string getCommandLine() const
Returns full command line, including arguments, for the process.
pid_t spawn(bool dismiss=false)
Spawn the new process.
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
util::ReconnectCtlPtr reconnectCtl()
The reconnect settings.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
virtual void makeReconnectCtl(const std::string &timer_name)
Instantiates a ReconnectCtl based on the connection's reconnect parameters.
static bool test_mode_
Test mode flag (default false).
static bool retry_
Flag which indicates if the database connection should be retried on fail.
void checkUnusable()
Throws an exception if the connection is not usable.
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
Exception thrown on failure to open database but permit retries.
Exception thrown on failure to open database.
Exception thrown on failure to execute a database function.
static void convertFromDatabaseTime(const MYSQL_TIME &expire, uint32_t valid_lifetime, time_t &cltt)
Converts Database Time to Lease Times.
static void convertToDatabaseTime(const time_t input_time, MYSQL_TIME &output_time)
Converts time_t value to database time.
Common MySQL Connector Pool.
static std::string KEA_ADMIN_
Holds location to kea-admin.
MySqlHolder mysql_
MySQL connection handle.
void prepareStatement(uint32_t index, const char *text)
Prepare Single Statement.
bool isTransactionStarted() const
Checks if there is a transaction in progress.
std::vector< std::string > text_statements_
Raw text of statements.
bool tls_
TLS flag (true when TLS was required, false otherwise).
static void convertToDatabaseTime(const time_t input_time, MYSQL_TIME &output_time)
Convert time_t value to database time.
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap ¶meters, const IOServiceAccessorPtr &ac=IOServiceAccessorPtr(), const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string())
Get the schema version.
static void convertFromDatabaseTime(const MYSQL_TIME &expire, uint32_t valid_lifetime, time_t &cltt)
Convert Database Time to Lease Times.
void commit()
Commits current transaction.
void startRecoverDbConnection()
The recover connection.
static void initializeSchema(const ParameterMap ¶meters)
Initialize schema.
static std::vector< std::string > toKeaAdminParameters(ParameterMap const ¶ms)
Convert MySQL library parameters to kea-admin parameters.
void openDatabase()
Open Database.
void prepareStatements(const TaggedStatement *start_statement, const TaggedStatement *end_statement)
Prepare statements.
int transaction_ref_count_
Reference counter for transactions.
void startTransaction()
Starts new transaction.
virtual ~MySqlConnection()
Destructor.
void rollback()
Rollbacks current transaction.
static void ensureSchemaVersion(const ParameterMap ¶meters, const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string())
Retrieve schema version, validate it against the hardcoded version, and attempt to initialize the sch...
~MySqlTransaction()
Destructor.
void commit()
Commits transaction.
MySqlTransaction(MySqlConnection &conn)
Constructor.
Exception thrown if name of database is not specified.
Thrown when an initialization of the schema failed.
We want to reuse the database backend connection and exchange code for other uses,...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
std::vector< std::string > ProcessEnvVars
Type of the container holding environment variables of the executable being run as a background proce...
const int DB_DBG_TRACE_DETAIL
Database logging levels.
const my_bool MLM_FALSE
MySQL false value.
const int MYSQL_DEFAULT_CONNECTION_TIMEOUT
@ MYSQL_START_TRANSACTION
@ MYSQL_INITIALIZE_SCHEMA
const uint32_t MYSQL_SCHEMA_VERSION_MAJOR
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
const uint32_t MYSQL_SCHEMA_VERSION_MINOR
bool my_bool
my_bool type in MySQL 8.x.
std::function< bool(util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
std::function< isc::asiolink::IOServicePtr()> IOServiceAccessor
Function which returns the IOService that can be used to recover the connection.
int MysqlExecuteStatement(MYSQL_STMT *stmt)
Execute a prepared statement.
bool isFile(string const &path)
Check if there is a file at the given path.
bool isDir(string const &path)
Check if there is a directory at the given path.
Defines the logger used by the top-level component of kea-lfc.
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
MySQL Selection Statements.