18#include <boost/lexical_cast.hpp>
24#include <unordered_map>
36int MySqlHolder::atexit_ = [] {
37 return atexit([] { mysql_library_end(); });
44 : conn_(conn), committed_(false) {
45 conn_.startTransaction();
67 const char* host =
"localhost";
76 unsigned int port = 0;
78 setIntParameterValue(
"port", 0, numeric_limits<uint16_t>::max(), port);
80 }
catch (
const std::exception& ex) {
84 const char* user = NULL;
93 const char* password = NULL;
97 password = spassword.c_str();
106 const char* name = NULL;
110 name = sname.c_str();
117 unsigned int read_timeout = 0;
118 unsigned int write_timeout = 0;
123 setIntParameterValue(
"connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
127 setIntParameterValue(
"read-timeout", 0, numeric_limits<int>::max(), read_timeout);
128 setIntParameterValue(
"write-timeout", 0, numeric_limits<int>::max(), write_timeout);
130 }
catch (
const std::exception& ex) {
134 const char* ca_file(0);
135 const char* ca_dir(0);
141 ca_dir = sca.c_str();
143 ca_file = sca.c_str();
149 const char* cert_file(0);
154 cert_file = scert.c_str();
159 const char* key_file(0);
164 key_file = skey.c_str();
169 const char* cipher_list(0);
174 cipher_list = scipher.c_str();
182#ifdef HAS_MYSQL_OPT_RECONNECT
189 result = mysql_options(
mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
197 const char *wait_time =
"SET SESSION wait_timeout = 30 * 86400";
198 result = mysql_options(
mysql_, MYSQL_INIT_COMMAND, wait_time);
208 const char *sql_mode =
"SET SESSION sql_mode ='STRICT_ALL_TABLES'";
209 result = mysql_options(
mysql_, MYSQL_INIT_COMMAND, sql_mode);
217 result = mysql_options(
mysql_, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
225 if (read_timeout > 0) {
226 result = mysql_options(
mysql_, MYSQL_OPT_READ_TIMEOUT, &read_timeout);
235 if (write_timeout > 0) {
236 result = mysql_options(
mysql_, MYSQL_OPT_WRITE_TIMEOUT, &write_timeout);
246 result = mysql_options(
mysql_, MYSQL_OPT_SSL_KEY, key_file);
251 result = mysql_options(
mysql_, MYSQL_OPT_SSL_CERT, cert_file);
256 result = mysql_options(
mysql_, MYSQL_OPT_SSL_CA, ca_file);
261 result = mysql_options(
mysql_, MYSQL_OPT_SSL_CAPATH, ca_dir);
266 result = mysql_options(
mysql_, MYSQL_OPT_SSL_CIPHER, cipher_list);
282 MYSQL* status = mysql_real_connect(
mysql_, host, user, password, name,
283 port, NULL, CLIENT_FOUND_ROWS);
288 std::string error_message = mysql_error(
mysql_);
296 std::ostringstream s;
298 s <<
" (scheduling retry " << rec->retryIndex() + 1 <<
" of " << rec->maxRetries() <<
" in " << rec->retryInterval() <<
" milliseconds)";
300 error_message += s.str();
316 if (autocommit_result != 0) {
329std::pair<uint32_t, uint32_t>
333 const string& timer_name,
338 if (!timer_name.empty()) {
346 MYSQL_STMT *stmt = mysql_stmt_init(conn.
mysql_);
349 "statement structure, reason: " << mysql_error(conn.
mysql_));
355 const char* version_sql =
"SELECT version, minor FROM schema_version";
356 int status = mysql_stmt_prepare(stmt, version_sql, strlen(version_sql));
359 << version_sql <<
">, reason: "
360 << mysql_error(conn.
mysql_));
366 << version_sql <<
">, reason: "
367 << mysql_errno(conn.
mysql_));
372 memset(bind, 0,
sizeof(bind));
375 bind[0].buffer_type = MYSQL_TYPE_LONG;
376 bind[0].is_unsigned = 1;
378 bind[0].buffer_length =
sizeof(
version);
381 bind[1].buffer_type = MYSQL_TYPE_LONG;
382 bind[1].is_unsigned = 1;
383 bind[1].buffer = &minor;
384 bind[1].buffer_length =
sizeof(minor);
386 if (mysql_stmt_bind_result(stmt, bind)) {
388 << version_sql <<
">, reason: "
389 << mysql_errno(conn.
mysql_));
393 if (mysql_stmt_fetch(stmt)) {
395 << version_sql <<
">, reason: "
396 << mysql_errno(conn.
mysql_));
400 mysql_stmt_close(stmt);
401 return (std::make_pair(
version, minor));
403 }
catch (
const std::exception&) {
405 mysql_stmt_close(stmt);
415 const string& timer_name) {
417 bool const retry(parameters.count(
"retry-on-startup") &&
418 parameters.at(
"retry-on-startup") ==
"true");
421 pair<uint32_t, uint32_t> schema_version;
423 schema_version =
getVersion(parameters, ac,
cb, retry ? timer_name :
string());
428 }
catch (exception
const& exception) {
444 schema_version =
getVersion(parameters, ac,
cb, retry ? timer_name :
string());
450 if (schema_version != expected_version) {
452 << expected_version.first <<
"." << expected_version.second
453 <<
", found version: " << schema_version.first <<
"."
454 << schema_version.second);
460 if (parameters.count(
"readonly") && parameters.at(
"readonly") ==
"true") {
475 kea_admin_parameters.insert(kea_admin_parameters.begin(),
"db-init");
482 pid_t
const pid(kea_admin.
spawn());
487 if (exit_code != 0) {
494 vector<string> result{
"mysql"};
495 for (
auto const& p : params) {
496 string const& keyword(p.first);
497 string const& value(p.second);
500 if (keyword ==
"user" ||
501 keyword ==
"password" ||
505 result.push_back(
"--" + keyword);
506 result.push_back(value);
513 static unordered_map<string, string> conversions{
514 {
"connect-timeout",
"connect_timeout"},
515 {
"cipher-list",
"ssl-cipher"},
516 {
"cert-file",
"ssl-cert"},
517 {
"key-file",
"ssl-key"},
518 {
"trust-anchor",
"ssl-ca"},
522 if (conversions.count(keyword)) {
523 result.push_back(
"--extra");
524 result.push_back(
"--" + conversions.at(keyword) +
" " + value);
541 if ((index >= statements_.size()) || (statements_[index] != NULL)) {
543 static_cast<int>(index) <<
") or indexed prepared " <<
544 "statement is not null");
549 statements_[index] = mysql_stmt_init(
mysql_);
550 if (statements_[index] == NULL) {
552 "statement structure, reason: " << mysql_error(
mysql_));
555 int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
558 text <<
">, reason: " << mysql_error(
mysql_));
567 tagged_statement != end_statement; ++tagged_statement) {
568 if (tagged_statement->index >= statements_.size()) {
569 statements_.resize(tagged_statement->index + 1, NULL);
574 tagged_statement->text);
583 for (
size_t i = 0; i < statements_.size(); ++i) {
584 if (statements_[i] != NULL) {
585 (void) mysql_stmt_close(statements_[i]);
586 statements_[i] = NULL;
605 MYSQL_TIME& output_time) {
611 const uint32_t valid_lifetime,
612 MYSQL_TIME& expire) {
618 uint32_t valid_lifetime, time_t& cltt) {
633 int status = mysql_query(
mysql_,
"START TRANSACTION");
636 "reason: " << mysql_error(
mysql_));
657 if (mysql_commit(
mysql_) != 0) {
675 if (mysql_rollback(
mysql_) != 0) {
683MySqlConnection::setIntParameterValue(
const std::string& name, int64_t min, int64_t max, T& value) {
690 if (svalue.empty()) {
695 auto parsed_value = boost::lexical_cast<T>(svalue);
697 if ((parsed_value < min) || (parsed_value > max)) {
698 isc_throw(BadValue,
"bad " << svalue <<
" value");
701 value = parsed_value;
707 isc_throw(BadValue, name <<
" parameter (" <<
708 svalue <<
") must be an integer between "
709 << min <<
" and " << max);
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.
std::string getCommandLine(std::unordered_set< std::string > redact_args={}) const
Returns full command line, including arguments, for the process.
bool isRunning(const pid_t pid) const
Checks if the process is still running.
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.
virtual void makeReconnectCtl(const std::string &timer_name, unsigned int id)
Instantiates a ReconnectCtl based on the connection's reconnect parameters.
void markUnusable()
Sets the unusable flag to true.
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.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
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.
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(), unsigned int id=0)
Get the schema version.
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 void convertFromDatabaseTime(const MYSQL_TIME &expire, uint32_t valid_lifetime, time_t &cltt)
Convert Database Time to Lease Times.
void commit()
Commits current transaction.
MySqlConnection(const ParameterMap ¶meters, IOServiceAccessorPtr io_accessor=IOServiceAccessorPtr(), DbCallback callback=DbCallback())
Constructor.
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.
int version()
returns Kea hooks version.
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.
static void check(const std::string &value)
Check if the value is a default credential.
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
MySQL Selection Statements.