Kea 3.3.1
mysql_connection.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
13#include <database/db_log.h>
16#include <util/filesystem.h>
17
18#include <boost/lexical_cast.hpp>
19
20#include <cstdint>
21#include <exception>
22#include <limits>
23#include <string>
24#include <unordered_map>
25
26using namespace isc;
27using namespace isc::asiolink;
28using namespace isc::data;
29using namespace std;
30
31namespace isc {
32namespace db {
33
34static MySqlLibraryInit init;
35
36std::string MySqlConnection::KEA_ADMIN_ = KEA_ADMIN;
37
39const int MYSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds
40
42 : conn_(conn), committed_(false) {
43 conn_.startTransaction();
44}
45
47 // Rollback if the MySqlTransaction::commit wasn't explicitly
48 // called.
49 if (!committed_) {
50 conn_.rollback();
51 }
52}
53
54void
56 conn_.commit();
57 committed_ = true;
58}
59
60// Open the database using the parameters passed to the constructor.
61
62void
64 // Set up the values of the parameters
65 const char* host = "localhost";
66 string shost;
67 try {
68 shost = getParameter("host");
69 host = shost.c_str();
70 } catch (...) {
71 // No host. Fine, we'll use "localhost"
72 }
73
74 unsigned int port = 0;
75 try {
76 setIntParameterValue("port", 0, numeric_limits<uint16_t>::max(), port);
77
78 } catch (const std::exception& ex) {
79 isc_throw(DbInvalidPort, ex.what());
80 }
81
82 const char* user = 0;
83 string suser;
84 try {
85 suser = getParameter("user");
86 user = suser.c_str();
87 } catch (...) {
88 // No user. Fine, we'll use null
89 }
90
91 const char* password = 0;
92 string spassword;
93 try {
94 spassword = getParameter("password");
95 password = spassword.c_str();
96 } catch (...) {
97 // No password. Fine, we'll use null
98 }
99 string spassword_file;
100 try {
101 spassword_file = getParameter("password-file");
102 } catch (...) {
103 // No password-file.
104 }
105 // Already tested by the parser: password and password-file are exclusive
106 if (!spassword_file.empty()) {
107 // This can throw.
108 spassword = util::file::getContent(spassword_file);
109 password = spassword.c_str();
110 }
111 if (password) {
112 // Refuse default password.
113 DefaultCredentials::check(spassword);
114 }
115
116 const char* name = 0;
117 string sname;
118 try {
119 sname = getParameter("name");
120 name = sname.c_str();
121 } catch (...) {
122 // No database name. Throw a "NoName" exception
123 isc_throw(NoDatabaseName, "must specify a name for the database");
124 }
125
126 unsigned int connect_timeout = MYSQL_DEFAULT_CONNECTION_TIMEOUT;
127 unsigned int read_timeout = 0;
128 unsigned int write_timeout = 0;
129 try {
130 // The timeout is only valid if greater than zero, as depending on the
131 // database, a zero timeout might signify something like "wait
132 // indefinitely".
133 setIntParameterValue("connect-timeout", 1, numeric_limits<int>::max(), connect_timeout);
134 // Other timeouts can be 0, meaning that the database client will follow a default
135 // behavior. Earlier MySQL versions didn't have these parameters, so we allow 0
136 // to skip setting them.
137 setIntParameterValue("read-timeout", 0, numeric_limits<int>::max(), read_timeout);
138 setIntParameterValue("write-timeout", 0, numeric_limits<int>::max(), write_timeout);
139
140 } catch (const std::exception& ex) {
141 isc_throw(DbInvalidTimeout, ex.what());
142 }
143
144 const char* ca_file(0);
145 const char* ca_dir(0);
146 string sca;
147 try {
148 sca = getParameter("trust-anchor");
149 tls_ = true;
150 if (util::file::isDir(sca)) {
151 ca_dir = sca.c_str();
152 } else {
153 ca_file = sca.c_str();
154 }
155 } catch (...) {
156 // No trust anchor
157 }
158
159 const char* cert_file(0);
160 string scert;
161 try {
162 scert = getParameter("cert-file");
163 tls_ = true;
164 cert_file = scert.c_str();
165 } catch (...) {
166 // No client certificate file
167 }
168
169 const char* key_file(0);
170 string skey;
171 try {
172 skey = getParameter("key-file");
173 tls_ = true;
174 key_file = skey.c_str();
175 } catch (...) {
176 // No private key file
177 }
178
179 const char* cipher_list(0);
180 string scipher;
181 try {
182 scipher = getParameter("cipher-list");
183 tls_ = true;
184 cipher_list = scipher.c_str();
185 } catch (...) {
186 // No cipher list
187 }
188
189 // Set options for the connection:
190 //
191 int result;
192#ifdef HAS_MYSQL_OPT_RECONNECT
193 // Though still supported by Mariadb (as of 11.5.0), MYSQL_OPT_RECONNECT is
194 // deprecated as of MySQL 8.0.34. Where it is still supported we should
195 // continue to ensure it is off. Enabling it leaves us with an unusable
196 // connection after a reconnect as among other things, it drops all our
197 // pre-compiled statements.
198 my_bool auto_reconnect = MLM_FALSE;
199 result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
200 if (result != 0) {
201 isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
202 mysql_error(mysql_));
203 }
204#endif
205
206 // Make sure we have a large idle time window ... say 30 days...
207 const char *wait_time = "SET SESSION wait_timeout = 30 * 86400";
208 result = mysql_options(mysql_, MYSQL_INIT_COMMAND, wait_time);
209 if (result != 0) {
210 isc_throw(DbOpenError, "unable to set wait_timeout " <<
211 mysql_error(mysql_));
212 }
213
214 // Set SQL mode options for the connection: SQL mode governs how what
215 // constitutes insertable data for a given column, and how to handle
216 // invalid data. We want to ensure we get the strictest behavior and
217 // to reject invalid data with an error.
218 const char *sql_mode = "SET SESSION sql_mode ='STRICT_ALL_TABLES'";
219 result = mysql_options(mysql_, MYSQL_INIT_COMMAND, sql_mode);
220 if (result != 0) {
221 isc_throw(DbOpenError, "unable to set SQL mode options: " <<
222 mysql_error(mysql_));
223 }
224
225 // Connection timeout, the amount of time taken for the client to drop
226 // the connection if the server is not responding.
227 result = mysql_options(mysql_, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
228 if (result != 0) {
229 isc_throw(DbOpenError, "unable to set database connection timeout: " <<
230 mysql_error(mysql_));
231 }
232
233 // Set the read timeout if it has been specified. Otherwise, the timeout is
234 // not used.
235 if (read_timeout > 0) {
236 result = mysql_options(mysql_, MYSQL_OPT_READ_TIMEOUT, &read_timeout);
237 if (result != 0) {
238 isc_throw(DbOpenError, "unable to set database read timeout: " <<
239 mysql_error(mysql_));
240 }
241 }
242
243 // Set the write timeout if it has been specified. Otherwise, the timeout
244 // is not used.
245 if (write_timeout > 0) {
246 result = mysql_options(mysql_, MYSQL_OPT_WRITE_TIMEOUT, &write_timeout);
247 if (result != 0) {
248 isc_throw(DbOpenError, "unable to set database write timeout: " <<
249 mysql_error(mysql_));
250 }
251 }
252
253 // If TLS is enabled set it. If something should go wrong it will happen
254 // later at the mysql_real_connect call.
255 if (tls_) {
256 result = mysql_options(mysql_, MYSQL_OPT_SSL_KEY, key_file);
257 if (result != 0) {
258 isc_throw(DbOpenError, "unable to set key: " << mysql_error(mysql_));
259 }
260
261 result = mysql_options(mysql_, MYSQL_OPT_SSL_CERT, cert_file);
262 if (result != 0) {
263 isc_throw(DbOpenError, "unable to set certificate: " << mysql_error(mysql_));
264 }
265
266 result = mysql_options(mysql_, MYSQL_OPT_SSL_CA, ca_file);
267 if (result != 0) {
268 isc_throw(DbOpenError, "unable to set CA: " << mysql_error(mysql_));
269 }
270
271 result = mysql_options(mysql_, MYSQL_OPT_SSL_CAPATH, ca_dir);
272 if (result != 0) {
273 isc_throw(DbOpenError, "unable to set CA path: " << mysql_error(mysql_));
274 }
275
276 result = mysql_options(mysql_, MYSQL_OPT_SSL_CIPHER, cipher_list);
277 if (result != 0) {
278 isc_throw(DbOpenError, "unable to set cipher: " << mysql_error(mysql_));
279 }
280 }
281
282 // Open the database.
283 //
284 // The option CLIENT_FOUND_ROWS is specified so that in an UPDATE,
285 // the affected rows are the number of rows found that match the
286 // WHERE clause of the SQL statement, not the rows changed. The reason
287 // here is that MySQL apparently does not update a row if data has not
288 // changed and so the "affected rows" (retrievable from MySQL) is zero.
289 // This makes it hard to distinguish whether the UPDATE changed no rows
290 // because no row matching the WHERE clause was found, or because a
291 // row was found but no data was altered.
292 MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
293 port, 0, CLIENT_FOUND_ROWS);
294 if (status != mysql_) {
295 // Mark this connection as no longer usable.
296 markUnusable();
297
298 std::string error_message = mysql_error(mysql_);
299
300 auto const& rec = reconnectCtl();
301 if (rec && DatabaseConnection::retry_) {
302
303 // Start the connection recovery.
305
306 std::ostringstream s;
307
308 s << " (scheduling retry " << rec->retryIndex() + 1 << " of " << rec->maxRetries() << " in " << rec->retryInterval() << " milliseconds)";
309
310 error_message += s.str();
311
312 isc_throw(DbOpenErrorWithRetry, error_message);
313 }
314
315 isc_throw(DbOpenError, error_message);
316 }
317
318 // Enable autocommit. In case transaction is explicitly used, this
319 // setting will be overwritten for the transaction. However, there are
320 // cases when lack of autocommit could cause transactions to hang
321 // until commit or rollback is explicitly called. This already
322 // caused issues for some unit tests which were unable to cleanup
323 // the database after the test because of pending transactions.
324 // Use of autocommit will eliminate this problem.
325 my_bool autocommit_result = mysql_autocommit(mysql_, 1);
326 if (autocommit_result != 0) {
327 isc_throw(DbOperationError, mysql_error(mysql_));
328 }
329
330 // To avoid a flush to disk on every commit, the global parameter
331 // innodb_flush_log_at_trx_commit should be set to 2. This will cause the
332 // changes to be written to the log, but flushed to disk in the background
333 // every second. Setting the parameter to that value will speed up the
334 // system, but at the risk of losing data if the system crashes.
335}
336
337// Get schema version.
338
339std::pair<uint32_t, uint32_t>
341 const IOServiceAccessorPtr& ac,
342 const DbCallback& cb,
343 const string& timer_name,
344 unsigned int id) {
345 // Get a connection.
346 MySqlConnection conn(parameters, ac, cb);
347
348 if (!timer_name.empty()) {
349 conn.makeReconnectCtl(timer_name, id);
350 }
351
352 // Open the database.
353 conn.openDatabase();
354
355 // Allocate a new statement.
356 MYSQL_STMT *stmt = mysql_stmt_init(conn.mysql_);
357 if (stmt == 0) {
358 isc_throw(DbOperationError, "unable to allocate MySQL prepared "
359 "statement structure, reason: " << mysql_error(conn.mysql_));
360 }
361
362 try {
363
364 // Prepare the statement from SQL text.
365 const char* version_sql = "SELECT version, minor FROM schema_version";
366 int status = mysql_stmt_prepare(stmt, version_sql, strlen(version_sql));
367 if (status != 0) {
368 isc_throw(DbOperationError, "unable to prepare MySQL statement <"
369 << version_sql << ">, reason: "
370 << mysql_error(conn.mysql_));
371 }
372
373 // Execute the prepared statement.
374 if (MysqlExecuteStatement(stmt) != 0) {
375 isc_throw(DbOperationError, "cannot execute schema version query <"
376 << version_sql << ">, reason: "
377 << mysql_errno(conn.mysql_));
378 }
379
380 // Bind the output of the statement to the appropriate variables.
381 MYSQL_BIND bind[2];
382 memset(bind, 0, sizeof(bind));
383
384 uint32_t version;
385 bind[0].buffer_type = MYSQL_TYPE_LONG;
386 bind[0].is_unsigned = 1;
387 bind[0].buffer = &version;
388 bind[0].buffer_length = sizeof(version);
389
390 uint32_t minor;
391 bind[1].buffer_type = MYSQL_TYPE_LONG;
392 bind[1].is_unsigned = 1;
393 bind[1].buffer = &minor;
394 bind[1].buffer_length = sizeof(minor);
395
396 if (mysql_stmt_bind_result(stmt, bind)) {
397 isc_throw(DbOperationError, "unable to bind result set for <"
398 << version_sql << ">, reason: "
399 << mysql_errno(conn.mysql_));
400 }
401
402 // Fetch the data.
403 if (mysql_stmt_fetch(stmt)) {
404 isc_throw(DbOperationError, "unable to bind result set for <"
405 << version_sql << ">, reason: "
406 << mysql_errno(conn.mysql_));
407 }
408
409 // Discard the statement and its resources
410 mysql_stmt_close(stmt);
411 return (std::make_pair(version, minor));
412
413 } catch (const std::exception&) {
414 // Avoid a memory leak on error.
415 mysql_stmt_close(stmt);
416
417 // Send the exception to the caller.
418 throw;
419 }
420}
421
422void
424 const DbCallback& cb,
425 const string& timer_name) {
426 // retry-on-startup?
427 bool const retry(parameters.count("retry-on-startup") &&
428 parameters.at("retry-on-startup") == "true");
429
431 pair<uint32_t, uint32_t> schema_version;
432 try {
433 schema_version = getVersion(parameters, ac, cb, retry ? timer_name : string());
434 } catch (DbOpenError const& exception) {
435 // Stop here. Initializing the schema won't work if we cannot create a connection to the
436 // database.
437 throw;
438 } catch (DbOpenErrorWithRetry const& exception) {
439 // Stop here. Initializing the schema won't work if we cannot create a connection to the
440 // database even as we are retrying.
441 throw;
442 } catch (DefaultCredential const& exception) {
443 // Stop here. Initializing the schema won't work if we cannot create a connection to the
444 // database due to default credentials being used.
445 throw;
446 } catch (exception const& exception) {
448
449 // Disable the recovery mechanism in test mode.
451 throw;
452 }
453 // This failure may occur for a variety of reasons. We are looking at
454 // initializing schema as the only potential mitigation. We could narrow
455 // down on the error that would suggest an uninitialized schema
456 // which would sound something along the lines of
457 // "table schema_version does not exist", but we do not necessarily have
458 // to. If the error had another cause, it will fail again during
459 // initialization or during the subsequent version retrieval and that is
460 // fine, and the error should still be relevant.
461 initializeSchema(parameters);
462
463 // Retrieve again because the initial retrieval failed.
464 schema_version = getVersion(parameters, ac, cb, retry ? timer_name : string());
465 }
466
467 // Check that the versions match.
468 pair<uint32_t, uint32_t> const expected_version(MYSQL_SCHEMA_VERSION_MAJOR,
470 if (schema_version != expected_version) {
471 isc_throw(DbOpenError, "MySQL schema version mismatch: expected version: "
472 << expected_version.first << "." << expected_version.second
473 << ", found version: " << schema_version.first << "."
474 << schema_version.second);
475 }
476}
477
478void
480 if (parameters.count("readonly") && parameters.at("readonly") == "true") {
481 // The readonly flag is historically used for host backends. Still, if
482 // enabled, it is a strong indication that we should not meDDLe with it.
484 return;
485 }
486
487 if (parameters.count("password-file")) {
488 // Kea-admin does not support the password-file argument.
490 return;
491 }
492
494 // It can happen for kea-admin to not exist, especially with
495 // packages that install it in a separate package.
497 return;
498 }
499
500 // Convert parameters.
501 vector<string> kea_admin_parameters(toKeaAdminParameters(parameters));
502 ProcessEnvVars const vars;
503 kea_admin_parameters.insert(kea_admin_parameters.begin(), "db-init");
504
505 // Run.
506 ProcessSpawn kea_admin(ProcessSpawn::SYNC, KEA_ADMIN_, kea_admin_parameters, vars,
507 /* inherit_env = */ true);
509 .arg(kea_admin.getCommandLine(std::unordered_set<std::string>{"--password"}));
510 pid_t const pid(kea_admin.spawn());
511 if (kea_admin.isRunning(pid)) {
512 isc_throw(SchemaInitializationFailed, "kea-admin still running");
513 }
514 int const exit_code(kea_admin.getExitStatus(pid));
515 if (exit_code != 0) {
516 isc_throw(SchemaInitializationFailed, "Expected exit code 0 for kea-admin. Got " << exit_code);
517 }
518}
519
520vector<string>
522 vector<string> result{"mysql"};
523 for (auto const& p : params) {
524 string const& keyword(p.first);
525 string const& value(p.second);
526
527 // These Kea parameters are the same as the kea-admin parameters.
528 if (keyword == "user" ||
529 keyword == "password" ||
530 keyword == "host" ||
531 keyword == "port" ||
532 keyword == "name") {
533 result.push_back("--" + keyword);
534 result.push_back(value);
535 continue;
536 }
537
538 // These Kea parameters do not have a direct kea-admin equivalent.
539 // But they do have a mariadb client flag equivalent.
540 // We pass them to kea-admin using the --extra flag.
541 static unordered_map<string, string> conversions{
542 {"connect-timeout", "connect_timeout"},
543 {"cipher-list", "ssl-cipher"},
544 {"cert-file", "ssl-cert"},
545 {"key-file", "ssl-key"},
546 {"trust-anchor", "ssl-ca"},
547 // {"read-timeout", "--net-read-timeout"}, // available in docs, but client says unknown variable?
548 // {"write-timeout", "--net-write-timeout"}, // available in docs, but client says unknown variable?
549 };
550 if (conversions.count(keyword)) {
551 result.push_back("--extra");
552 result.push_back("--" + conversions.at(keyword) + " " + value);
553 }
554 }
555 return result;
556}
557
558// Prepared statement setup. The textual form of an SQL statement is stored
559// in a vector of strings (text_statements_) and is used in the output of
560// error messages. The SQL statement is also compiled into a "prepared
561// statement" (stored in statements_), which avoids the overhead of compilation
562// during use. As prepared statements have resources allocated to them, the
563// class destructor explicitly destroys them.
564
565void
566MySqlConnection::prepareStatement(uint32_t index, const char* text) {
567 // Validate that there is space for the statement in the statements array
568 // and that nothing has been placed there before.
569 if ((index >= statements_.size()) || (statements_[index] != 0)) {
570 isc_throw(InvalidParameter, "invalid prepared statement index (" <<
571 static_cast<int>(index) << ") or indexed prepared " <<
572 "statement is not null");
573 }
574
575 // All OK, so prepare the statement
576 text_statements_[index] = std::string(text);
577 statements_[index] = mysql_stmt_init(mysql_);
578 if (statements_[index] == 0) {
579 isc_throw(DbOperationError, "unable to allocate MySQL prepared "
580 "statement structure, reason: " << mysql_error(mysql_));
581 }
582
583 int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
584 if (status != 0) {
585 isc_throw(DbOperationError, "unable to prepare MySQL statement <" <<
586 text << ">, reason: " << mysql_error(mysql_));
587 }
588}
589
590void
592 const TaggedStatement* end_statement) {
593 // Created the MySQL prepared statements for each DML statement.
594 for (const TaggedStatement* tagged_statement = start_statement;
595 tagged_statement != end_statement; ++tagged_statement) {
596 if (tagged_statement->index >= statements_.size()) {
597 statements_.resize(tagged_statement->index + 1, 0);
598 text_statements_.resize(tagged_statement->index + 1,
599 std::string(""));
600 }
601 prepareStatement(tagged_statement->index,
602 tagged_statement->text);
603 }
604}
605
608 // Free up the prepared statements, ignoring errors. (What would we do
609 // about them? We're destroying this object and are not really concerned
610 // with errors on a database connection that is about to go away.)
611 for (size_t i = 0; i < statements_.size(); ++i) {
612 if (statements_[i] != 0) {
613 (void) mysql_stmt_close(statements_[i]);
614 statements_[i] = 0;
615 }
616 }
617 statements_.clear();
618 text_statements_.clear();
619}
620
621// Time conversion methods.
622//
623// Note that the MySQL TIMESTAMP data type (used for "expire") converts data
624// from the current timezone to UTC for storage, and from UTC to the current
625// timezone for retrieval.
626//
627// This causes no problems providing that:
628// a) cltt is given in local time
629// b) We let the system take care of timezone conversion when converting
630// from a time read from the database into a local time.
631void
633 MYSQL_TIME& output_time) {
634 MySqlBinding::convertToDatabaseTime(input_time, output_time);
635}
636
637void
639 const uint32_t valid_lifetime,
640 MYSQL_TIME& expire) {
641 MySqlBinding::convertToDatabaseTime(cltt, valid_lifetime, expire);
642}
643
644void
646 uint32_t valid_lifetime, time_t& cltt) {
647 MySqlBinding::convertFromDatabaseTime(expire, valid_lifetime, cltt);
648}
649
650void
652 // If it is nested transaction, do nothing.
653 if (++transaction_ref_count_ > 1) {
654 return;
655 }
656
659 // We create prepared statements for all other queries, but MySQL
660 // don't support prepared statements for START TRANSACTION.
661 int status = mysql_query(mysql_, "START TRANSACTION");
662 if (status != 0) {
663 isc_throw(DbOperationError, "unable to start transaction, "
664 "reason: " << mysql_error(mysql_));
665 }
666}
667
668bool
672
673void
675 if (transaction_ref_count_ <= 0) {
676 isc_throw(Unexpected, "commit called for not started transaction - coding error");
677 }
678
679 // When committing nested transaction, do nothing.
680 if (--transaction_ref_count_ > 0) {
681 return;
682 }
685 if (mysql_commit(mysql_) != 0) {
686 isc_throw(DbOperationError, "commit failed: "
687 << mysql_error(mysql_));
688 }
689}
690
691void
693 if (transaction_ref_count_ <= 0) {
694 isc_throw(Unexpected, "rollback called for not started transaction - coding error");
695 }
696
697 // When rolling back nested transaction, do nothing.
698 if (--transaction_ref_count_ > 0) {
699 return;
700 }
703 if (mysql_rollback(mysql_) != 0) {
704 isc_throw(DbOperationError, "rollback failed: "
705 << mysql_error(mysql_));
706 }
707}
708
709template<typename T>
710void
711MySqlConnection::setIntParameterValue(const std::string& name, int64_t min, int64_t max, T& value) {
712 string svalue;
713 try {
714 svalue = getParameter(name);
715 } catch (...) {
716 // Do nothing if the parameter is not present.
717 }
718 if (svalue.empty()) {
719 return;
720 }
721 try {
722 // Try to convert the value.
723 auto parsed_value = boost::lexical_cast<T>(svalue);
724 // Check if the value is within the specified range.
725 if ((parsed_value < min) || (parsed_value > max)) {
726 isc_throw(BadValue, "bad " << svalue << " value");
727 }
728 // Everything is fine. Return the parsed value.
729 value = parsed_value;
730
731 } catch (...) {
732 // We may end up here when lexical_cast fails or when the
733 // parsed value is not within the desired range. In both
734 // cases let's throw the same general error.
735 isc_throw(BadValue, name << " parameter (" <<
736 svalue << ") must be an integer between "
737 << min << " and " << max);
738 }
739}
740
741} // namespace db
742} // namespace isc
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.
Exception thrown on attempt to use a default credential.
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.
Invalid port number.
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 &parameters, 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 &parameters, IOServiceAccessorPtr io_accessor=IOServiceAccessorPtr(), DbCallback callback=DbCallback())
Constructor.
void startRecoverDbConnection()
The recover connection.
static void initializeSchema(const ParameterMap &parameters)
Initialize schema.
static std::vector< std::string > toKeaAdminParameters(ParameterMap const &params)
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 &parameters, 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...
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.
const int DB_DBG_TRACE_DETAIL
Database logging levels.
Definition db_log.cc:21
const my_bool MLM_FALSE
MySQL false value.
const int MYSQL_DEFAULT_CONNECTION_TIMEOUT
@ MYSQL_START_TRANSACTION
Definition db_log.h:74
@ MYSQL_NO_INIT_NO_ADMIN
Definition db_log.h:70
@ MYSQL_INITIALIZE_SCHEMA
Definition db_log.h:69
@ MYSQL_NO_INIT_NO_PASSWORD
Definition db_log.h:71
@ MYSQL_INITIAL_CONNECTION_FAIL
Definition db_log.h:68
@ MYSQL_ROLLBACK
Definition db_log.h:76
@ MYSQL_NO_INIT_READONLY
Definition db_log.h:72
@ MYSQL_COMMIT
Definition db_log.h:75
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.
string getContent(string const &file_name)
Get the content of a regular file.
Definition filesystem.cc:33
bool isFile(string const &path)
Check if there is a file at the given path.
Definition filesystem.cc:80
bool isDir(string const &path)
Check if there is a directory at the given path.
Definition filesystem.cc:71
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.
Definition db_log.h:152
Structure used to initialize and clean up after MySQL library.
MySQL Selection Statements.