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