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");
 
  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)) {
 
  697            isc_throw(BadValue, 
"bad " << svalue << 
" value");
 
  700        value = parsed_value;
 
  706        isc_throw(BadValue, name << 
" parameter (" <<
 
  707                  svalue << 
") must be an integer between " 
  708                  << 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.
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.
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.