18#include <boost/lexical_cast.hpp>
24#include <unordered_map>
36int MySqlHolder::atexit_ = [] {
37 return atexit([] { mysql_library_end(); });
44 : conn_(conn), committed_(false) {
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");
481 pid_t
const pid(kea_admin.
spawn());
486 if (exit_code != 0) {
493 vector<string> result{
"mysql"};
494 for (
auto const& p : params) {
495 string const& keyword(p.first);
496 string const& value(p.second);
499 if (keyword ==
"user" ||
500 keyword ==
"password" ||
504 result.push_back(
"--" + keyword);
505 result.push_back(value);
512 static unordered_map<string, string> conversions{
513 {
"connect-timeout",
"connect_timeout"},
514 {
"cipher-list",
"ssl-cipher"},
515 {
"cert-file",
"ssl-cert"},
516 {
"key-file",
"ssl-key"},
517 {
"trust-anchor",
"ssl-ca"},
521 if (conversions.count(keyword)) {
522 result.push_back(
"--extra");
523 result.push_back(
"--" + conversions.at(keyword) +
" " + value);
540 if ((index >= statements_.size()) || (statements_[index] != NULL)) {
542 static_cast<int>(index) <<
") or indexed prepared " <<
543 "statement is not null");
548 statements_[index] = mysql_stmt_init(
mysql_);
549 if (statements_[index] == NULL) {
551 "statement structure, reason: " << mysql_error(
mysql_));
554 int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
557 text <<
">, reason: " << mysql_error(
mysql_));
566 tagged_statement != end_statement; ++tagged_statement) {
567 if (tagged_statement->index >= statements_.size()) {
568 statements_.resize(tagged_statement->index + 1, NULL);
573 tagged_statement->text);
582 for (
size_t i = 0; i < statements_.size(); ++i) {
583 if (statements_[i] != NULL) {
584 (void) mysql_stmt_close(statements_[i]);
585 statements_[i] = NULL;
604 MYSQL_TIME& output_time) {
610 const uint32_t valid_lifetime,
611 MYSQL_TIME& expire) {
617 uint32_t valid_lifetime, time_t& cltt) {
632 int status = mysql_query(
mysql_,
"START TRANSACTION");
635 "reason: " << mysql_error(
mysql_));
656 if (mysql_commit(
mysql_) != 0) {
674 if (mysql_rollback(
mysql_) != 0) {
682MySqlConnection::setIntParameterValue(
const std::string& name, int64_t min, int64_t max, T& value) {
689 if (svalue.empty()) {
694 auto parsed_value = boost::lexical_cast<T>(svalue);
696 if ((parsed_value < min) || (parsed_value > max)) {
700 value = parsed_value;
707 svalue <<
") must be an integer between "
708 << 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.
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.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
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.
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.
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.
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.