Kea: mysql_connection.cc Source File

| 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
9#include <asiolink/io_service.h>
10#include <asiolink/process_spawn.h>
11#include <cc/default_credentials.h>
12#include <database/database_connection.h>
13#include <database/db_log.h>
14#include <exceptions/exceptions.h>
15#include <mysql/mysql_connection.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
39 const int MYSQL_DEFAULT_CONNECTION_TIMEOUT = 5; // seconds
40
41MySqlTransaction::MySqlTransaction(MySqlConnection& conn)
42 : conn_(conn), committed_(false) {
43 conn_.startTransaction();
44}
45
46MySqlTransaction::~MySqlTransaction() {
47 // Rollback if the MySqlTransaction::commit wasn't explicitly
48 // called.
49 if (!committed_) {
50 conn_.rollback();
51 }
52}
53
54void
55MySqlTransaction::commit() {
56 conn_.commit();
57 committed_ = true;
58}
59
60// Open the database using the parameters passed to the constructor.
61
62void
63MySqlConnection::openDatabase() {
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.
292 startRecoverDbConnection();
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>
328MySqlConnection::getVersion(const ParameterMap& parameters,
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
411MySqlConnection::ensureSchemaVersion(const ParameterMap& parameters,
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
418 IOServiceAccessorPtr ac(new IOServiceAccessor(&DatabaseConnection::getIOService));
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) {
435 DB_LOG_WARN(MYSQL_INITIAL_CONNECTION_FAIL).arg(exception.what());
436
437 // Disable the recovery mechanism in test mode.
438 if (DatabaseConnection::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,
457 MYSQL_SCHEMA_VERSION_MINOR);
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
467MySqlConnection::initializeSchema(const ParameterMap& parameters) {
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.
471 DB_LOG_WARN(MYSQL_NO_INIT_READONLY).arg();
472 return;
473 }
474
475 if (!isc::util::file::isFile(KEA_ADMIN_)) {
476 // It can happen for kea-admin to not exist, especially with
477 // packages that install it in a separate package.
478 DB_LOG_WARN(MYSQL_NO_INIT_NO_ADMIN).arg();
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);
490 DB_LOG_INFO(MYSQL_INITIALIZE_SCHEMA)
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>
503MySqlConnection::toKeaAdminParameters(ParameterMap const& params) {
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
573MySqlConnection::prepareStatements(const TaggedStatement* start_statement,
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
589MySqlConnection::~MySqlConnection() {
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
614MySqlConnection::convertToDatabaseTime(const time_t input_time,
615 MYSQL_TIME& output_time) {
616 MySqlBinding::convertToDatabaseTime(input_time, output_time);
617}
618
619void
620 MySqlConnection::convertToDatabaseTime(const time_t cltt,
621 const uint32_t valid_lifetime,
622 MYSQL_TIME& expire) {
623 MySqlBinding::convertToDatabaseTime(cltt, valid_lifetime, expire);
624}
625
626void
627MySqlConnection::convertFromDatabaseTime(const MYSQL_TIME& expire,
628 uint32_t valid_lifetime, time_t& cltt) {
629 MySqlBinding::convertFromDatabaseTime(expire, valid_lifetime, cltt);
630}
631
632void
633MySqlConnection::startTransaction() {
634 // If it is nested transaction, do nothing.
635 if (++transaction_ref_count_ > 1) {
636 return;
637 }
638
639 DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_START_TRANSACTION);
640 checkUnusable();
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
651MySqlConnection::isTransactionStarted() const {
652 return (transaction_ref_count_ > 0);
653}
654
655void
656MySqlConnection::commit() {
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 }
665 DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_COMMIT);
666 checkUnusable();
667 if (mysql_commit(mysql_) != 0) {
668 isc_throw(DbOperationError, "commit failed: "
669 << mysql_error(mysql_));
670 }
671}
672
673void
674MySqlConnection::rollback() {
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 }
683 DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, MYSQL_ROLLBACK);
684 checkUnusable();
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
isc::InvalidParameter
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
Definition exceptions/exceptions.h:124
isc::Unexpected
A generic exception that is thrown when an unexpected error condition occurs.
Definition exceptions/exceptions.h:153
isc::asiolink::ProcessSpawn
Utility class for spawning new processes.
Definition process_spawn.h:63
isc::asiolink::ProcessSpawn::getExitStatus
int getExitStatus(const pid_t pid) const
Returns exit status of the process.
Definition process_spawn.cc:525
isc::asiolink::ProcessSpawn::getCommandLine
std::string getCommandLine(std::unordered_set< std::string > redact_args={}) const
Returns full command line, including arguments, for the process.
Definition process_spawn.cc:505
isc::asiolink::ProcessSpawn::isRunning
bool isRunning(const pid_t pid) const
Checks if the process is still running.
Definition process_spawn.cc:515
isc::asiolink::ProcessSpawn::spawn
pid_t spawn(bool dismiss=false)
Spawn the new process.
Definition process_spawn.cc:510
isc::asiolink::ProcessSpawn::SYNC
@ SYNC
Definition process_spawn.h:69
isc::data::DefaultCredential
Exception thrown on attempt to use a default credential.
Definition default_credentials.h:18
isc::db::DatabaseConnection::getParameter
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
Definition database_connection.cc:32
isc::db::DatabaseConnection::reconnectCtl
util::ReconnectCtlPtr reconnectCtl()
The reconnect settings.
Definition database_connection.h:162
isc::db::DatabaseConnection::makeReconnectCtl
virtual void makeReconnectCtl(const std::string &timer_name, unsigned int id)
Instantiates a ReconnectCtl based on the connection's reconnect parameters.
Definition database_connection.cc:155
isc::db::DatabaseConnection::markUnusable
void markUnusable()
Sets the unusable flag to true.
Definition database_connection.h:302
isc::db::DatabaseConnection::test_mode_
static bool test_mode_
Test mode flag (default false).
Definition database_connection.h:280
isc::db::DatabaseConnection::retry_
static bool retry_
Flag which indicates if the database connection should be retried on fail.
Definition database_connection.h:262
isc::db::DatabaseConnection::checkUnusable
void checkUnusable()
Throws an exception if the connection is not usable.
Definition database_connection.h:266
isc::db::DatabaseConnection::getIOService
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
Definition database_connection.h:240
isc::db::DatabaseConnection::ParameterMap
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Definition database_connection.h:137
isc::db::DbInvalidPort
Invalid port number.
Definition database_connection.h:80
isc::db::DbInvalidTimeout
Invalid Timeout.
Definition database_connection.h:71
isc::db::DbOpenErrorWithRetry
Exception thrown on failure to open database but permit retries.
Definition database_connection.h:38
isc::db::DbOpenError
Exception thrown on failure to open database.
Definition database_connection.h:31
isc::db::DbOperationError
Exception thrown on failure to execute a database function.
Definition database_connection.h:45
isc::db::MySqlBinding::convertFromDatabaseTime
static void convertFromDatabaseTime(const MYSQL_TIME &expire, uint32_t valid_lifetime, time_t &cltt)
Converts Database Time to Lease Times.
Definition mysql_binding.cc:273
isc::db::MySqlBinding::convertToDatabaseTime
static void convertToDatabaseTime(const time_t input_time, MYSQL_TIME &output_time)
Converts time_t value to database time.
Definition mysql_binding.cc:191
isc::db::MySqlConnection
Common MySQL Connector Pool.
Definition mysql_connection.h:235
isc::db::MySqlConnection::KEA_ADMIN_
static std::string KEA_ADMIN_
Holds location to kea-admin.
Definition mysql_connection.h:836
isc::db::MySqlConnection::mysql_
MySqlHolder mysql_
MySQL connection handle.
Definition mysql_connection.h:809
isc::db::MySqlConnection::getVersion
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.
Definition mysql_connection.cc:328
isc::db::MySqlConnection::prepareStatement
void prepareStatement(uint32_t index, const char *text)
Prepare Single Statement.
Definition mysql_connection.cc:548
isc::db::MySqlConnection::isTransactionStarted
bool isTransactionStarted() const
Checks if there is a transaction in progress.
Definition mysql_connection.cc:651
isc::db::MySqlConnection::text_statements_
std::vector< std::string > text_statements_
Raw text of statements.
Definition mysql_connection.h:803
isc::db::MySqlConnection::tls_
bool tls_
TLS flag (true when TLS was required, false otherwise).
Definition mysql_connection.h:832
isc::db::MySqlConnection::convertToDatabaseTime
static void convertToDatabaseTime(const time_t input_time, MYSQL_TIME &output_time)
Convert time_t value to database time.
Definition mysql_connection.cc:614
isc::db::MySqlConnection::convertFromDatabaseTime
static void convertFromDatabaseTime(const MYSQL_TIME &expire, uint32_t valid_lifetime, time_t &cltt)
Convert Database Time to Lease Times.
Definition mysql_connection.cc:627
isc::db::MySqlConnection::commit
void commit()
Commits current transaction.
Definition mysql_connection.cc:656
isc::db::MySqlConnection::MySqlConnection
MySqlConnection(const ParameterMap &parameters, IOServiceAccessorPtr io_accessor=IOServiceAccessorPtr(), DbCallback callback=DbCallback())
Constructor.
Definition mysql_connection.h:248
isc::db::MySqlConnection::startRecoverDbConnection
void startRecoverDbConnection()
The recover connection.
Definition mysql_connection.h:744
isc::db::MySqlConnection::initializeSchema
static void initializeSchema(const ParameterMap &parameters)
Initialize schema.
Definition mysql_connection.cc:467
isc::db::MySqlConnection::toKeaAdminParameters
static std::vector< std::string > toKeaAdminParameters(ParameterMap const &params)
Convert MySQL library parameters to kea-admin parameters.
Definition mysql_connection.cc:503
isc::db::MySqlConnection::openDatabase
void openDatabase()
Open Database.
Definition mysql_connection.cc:63
isc::db::MySqlConnection::prepareStatements
void prepareStatements(const TaggedStatement *start_statement, const TaggedStatement *end_statement)
Prepare statements.
Definition mysql_connection.cc:573
isc::db::MySqlConnection::transaction_ref_count_
int transaction_ref_count_
Reference counter for transactions.
Definition mysql_connection.h:829
isc::db::MySqlConnection::startTransaction
void startTransaction()
Starts new transaction.
Definition mysql_connection.cc:633
isc::db::MySqlConnection::~MySqlConnection
virtual ~MySqlConnection()
Destructor.
Definition mysql_connection.cc:589
isc::db::MySqlConnection::rollback
void rollback()
Rollbacks current transaction.
Definition mysql_connection.cc:674
isc::db::MySqlConnection::ensureSchemaVersion
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...
Definition mysql_connection.cc:411
isc::db::MySqlTransaction::~MySqlTransaction
~MySqlTransaction()
Destructor.
Definition mysql_connection.cc:46
isc::db::MySqlTransaction::commit
void commit()
Commits transaction.
Definition mysql_connection.cc:55
isc::db::MySqlTransaction::MySqlTransaction
MySqlTransaction(MySqlConnection &conn)
Constructor.
Definition mysql_connection.cc:41
isc::db::NoDatabaseName
Exception thrown if name of database is not specified.
Definition database_connection.h:24
isc::db::SchemaInitializationFailed
Thrown when an initialization of the schema failed.
Definition database_connection.h:96
version
int version()
returns Kea hooks version.
Definition d2/gss_tsig/version.cc:13
database_connection.h
db_log.h
We want to reuse the database backend connection and exchange code for other uses,...
default_credentials.h
exceptions.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
filesystem.h
io_service.h
mysql_connection.h
isc::asiolink
Definition addr_utilities.cc:170
isc::asiolink::ProcessEnvVars
std::vector< std::string > ProcessEnvVars
Type of the container holding environment variables of the executable being run as a background proce...
Definition process_spawn.h:43
isc::cb
Definition base_config_backend.h:18
isc::data
Definition base_stamped_element.cc:12
isc::db
Definition audit_entry.cc:14
isc::db::DB_DBG_TRACE_DETAIL
const int DB_DBG_TRACE_DETAIL
Database logging levels.
Definition db_log.cc:21
isc::db::MLM_FALSE
const my_bool MLM_FALSE
MySQL false value.
Definition mysql_constants.h:36
isc::db::MYSQL_DEFAULT_CONNECTION_TIMEOUT
const int MYSQL_DEFAULT_CONNECTION_TIMEOUT
Definition mysql_connection.cc:39
isc::db::MYSQL_START_TRANSACTION
@ MYSQL_START_TRANSACTION
Definition db_log.h:72
isc::db::MYSQL_NO_INIT_NO_ADMIN
@ MYSQL_NO_INIT_NO_ADMIN
Definition db_log.h:69
isc::db::MYSQL_INITIALIZE_SCHEMA
@ MYSQL_INITIALIZE_SCHEMA
Definition db_log.h:68
isc::db::MYSQL_INITIAL_CONNECTION_FAIL
@ MYSQL_INITIAL_CONNECTION_FAIL
Definition db_log.h:67
isc::db::MYSQL_ROLLBACK
@ MYSQL_ROLLBACK
Definition db_log.h:74
isc::db::MYSQL_NO_INIT_READONLY
@ MYSQL_NO_INIT_READONLY
Definition db_log.h:70
isc::db::MYSQL_COMMIT
@ MYSQL_COMMIT
Definition db_log.h:73
isc::db::MYSQL_SCHEMA_VERSION_MAJOR
const uint32_t MYSQL_SCHEMA_VERSION_MAJOR
Definition mysql_constants.h:55
isc::db::IOServiceAccessorPtr
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
Definition database_connection.h:115
isc::db::MYSQL_SCHEMA_VERSION_MINOR
const uint32_t MYSQL_SCHEMA_VERSION_MINOR
Definition mysql_constants.h:56
isc::db::my_bool
bool my_bool
my_bool type in MySQL 8.x.
Definition mysql_constants.h:33
isc::db::DbCallback
std::function< bool(util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
Definition database_connection.h:103
isc::db::IOServiceAccessor
std::function< isc::asiolink::IOServicePtr()> IOServiceAccessor
Function which returns the IOService that can be used to recover the connection.
Definition database_connection.h:112
isc::db::MysqlExecuteStatement
int MysqlExecuteStatement(MYSQL_STMT *stmt)
Execute a prepared statement.
Definition mysql_connection.h:106
isc::util::file::isFile
bool isFile(string const &path)
Check if there is a file at the given path.
Definition filesystem.cc:80
isc::util::file::isDir
bool isDir(string const &path)
Check if there is a directory at the given path.
Definition filesystem.cc:71
isc
Defines the logger used by the top-level component of kea-lfc.
Definition check_exists_add.cc:18
process_spawn.h
isc::data::DefaultCredentials::check
static void check(const std::string &value)
Check if the value is a default credential.
Definition default_credentials.cc:27
isc::db::DB_LOG_DEBUG
Definition db_log.h:207
isc::db::DB_LOG_INFO
Definition db_log.h:202
isc::db::DB_LOG_WARN
Definition db_log.h:197
isc::db::DB_LOG::arg
DB_LOG & arg(T first, Args... args)
Pass parameters to replace logger placeholders.
Definition db_log.h:150
isc::db::MySqlLibraryInit
Structure used to initialize and clean up after MySQL library.
Definition mysql_connection.h:840
isc::db::TaggedStatement
MySQL Selection Statements.
Definition mysql_connection.h:74