Kea 2.7.1
mysql_host_data_source.cc
Go to the documentation of this file.
1// Copyright (C) 2015-2024 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
11#include <dhcp/libdhcp++.h>
12#include <dhcp/option.h>
14#include <dhcp/option_space.h>
16#include <dhcpsrv/cfg_option.h>
17#include <dhcpsrv/cfgmgr.h>
18#include <dhcpsrv/dhcpsrv_log.h>
19#include <dhcpsrv/host_mgr.h>
21#include <dhcpsrv/timer_mgr.h>
22#include <util/buffer.h>
24#include <util/optional.h>
25
26#include <boost/algorithm/string/split.hpp>
27#include <boost/algorithm/string/classification.hpp>
28#include <boost/array.hpp>
29#include <boost/foreach.hpp>
30#include <boost/pointer_cast.hpp>
31#include <boost/static_assert.hpp>
32
33#include <mysql.h>
34#include <mysqld_error.h>
35#include <stdint.h>
36
37#include <mutex>
38#include <string>
39
40using namespace isc;
41using namespace isc::asiolink;
42using namespace isc::db;
43using namespace isc::dhcp;
44using namespace isc::util;
45using namespace isc::data;
46using namespace std;
47
48namespace {
49
54const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
55
84class MySqlHostExchange {
85private:
90
91 static const size_t HOST_ID_COL = 0;
92 static const size_t DHCP_IDENTIFIER_COL = 1;
93 static const size_t DHCP_IDENTIFIER_TYPE_COL = 2;
94 static const size_t DHCP4_SUBNET_ID_COL = 3;
95 static const size_t DHCP6_SUBNET_ID_COL = 4;
96 static const size_t IPV4_ADDRESS_COL = 5;
97 static const size_t HOSTNAME_COL = 6;
98 static const size_t DHCP4_CLIENT_CLASSES_COL = 7;
99 static const size_t DHCP6_CLIENT_CLASSES_COL = 8;
100 static const size_t USER_CONTEXT_COL = 9;
101 static const size_t DHCP4_NEXT_SERVER_COL = 10;
102 static const size_t DHCP4_SERVER_HOSTNAME_COL = 11;
103 static const size_t DHCP4_BOOT_FILE_NAME_COL = 12;
104 static const size_t AUTH_KEY_COL = 13;
106
107 static const size_t HOST_COLUMNS = 14;
108
109public:
110
117 MySqlHostExchange(const size_t additional_columns_num = 0)
118 : columns_num_(HOST_COLUMNS + additional_columns_num),
119 bind_(columns_num_), columns_(columns_num_),
120 error_(columns_num_, MLM_FALSE), host_id_(0),
121 dhcp_identifier_length_(0), dhcp_identifier_type_(0),
122 dhcp4_subnet_id_(SUBNET_ID_UNUSED),
123 dhcp6_subnet_id_(SUBNET_ID_UNUSED), ipv4_address_(0),
124 hostname_length_(0), dhcp4_client_classes_length_(0),
125 dhcp6_client_classes_length_(0),
126 user_context_length_(0),
127 dhcp4_next_server_(0),
128 dhcp4_server_hostname_length_(0),
129 dhcp4_boot_file_name_length_(0),
130 auth_key_length_(0),
131 dhcp4_subnet_id_null_(MLM_FALSE),
132 dhcp6_subnet_id_null_(MLM_FALSE),
133 ipv4_address_null_(MLM_FALSE), hostname_null_(MLM_FALSE),
134 dhcp4_client_classes_null_(MLM_FALSE),
135 dhcp6_client_classes_null_(MLM_FALSE),
136 user_context_null_(MLM_FALSE),
137 dhcp4_next_server_null_(MLM_FALSE),
138 dhcp4_server_hostname_null_(MLM_FALSE),
139 dhcp4_boot_file_name_null_(MLM_FALSE),
140 auth_key_null_(MLM_FALSE) {
141
142 // Fill arrays with 0 so as they don't include any garbage.
143 memset(dhcp_identifier_buffer_, 0, sizeof(dhcp_identifier_buffer_));
144 memset(hostname_, 0, sizeof(hostname_));
145 memset(dhcp4_client_classes_, 0, sizeof(dhcp4_client_classes_));
146 memset(dhcp6_client_classes_, 0, sizeof(dhcp6_client_classes_));
147 memset(user_context_, 0, sizeof(user_context_));
148 memset(dhcp4_server_hostname_, 0, sizeof(dhcp4_server_hostname_));
149 memset(dhcp4_boot_file_name_, 0, sizeof(dhcp4_boot_file_name_));
150 memset(auth_key_, 0, sizeof(auth_key_));
151
152 // Set the column names for use by this class. This only comprises
153 // names used by the MySqlHostExchange class. Derived classes will
154 // need to set names for the columns they use.
155 columns_[HOST_ID_COL] = "host_id";
156 columns_[DHCP_IDENTIFIER_COL] = "dhcp_identifier";
157 columns_[DHCP_IDENTIFIER_TYPE_COL] = "dhcp_identifier_type";
158 columns_[DHCP4_SUBNET_ID_COL] = "dhcp4_subnet_id";
159 columns_[DHCP6_SUBNET_ID_COL] = "dhcp6_subnet_id";
160 columns_[IPV4_ADDRESS_COL] = "ipv4_address";
161 columns_[HOSTNAME_COL] = "hostname";
162 columns_[DHCP4_CLIENT_CLASSES_COL] = "dhcp4_client_classes";
163 columns_[DHCP6_CLIENT_CLASSES_COL] = "dhcp6_client_classes";
164 columns_[USER_CONTEXT_COL] = "user_context";
165 columns_[DHCP4_NEXT_SERVER_COL] = "dhcp4_next_server";
166 columns_[DHCP4_SERVER_HOSTNAME_COL] = "dhcp4_server_hostname";
167 columns_[DHCP4_BOOT_FILE_NAME_COL] = "dhcp4_boot_file_name";
168 columns_[AUTH_KEY_COL] = "auth_key";
169
170 BOOST_STATIC_ASSERT(13 < HOST_COLUMNS);
171 }
172
174 virtual ~MySqlHostExchange() {
175 }
176
191 size_t findAvailColumn() const {
192 std::vector<std::string>::const_iterator empty_column =
193 std::find(columns_.begin(), columns_.end(), std::string());
194 return (std::distance(columns_.begin(), empty_column));
195 }
196
200 uint64_t getHostId() const {
201 return (host_id_);
202 }
203
214 static void setErrorIndicators(std::vector<MYSQL_BIND>& bind,
215 std::vector<my_bools>& error) {
216 for (size_t i = 0; i < error.size(); ++i) {
217 error[i] = MLM_FALSE;
218 bind[i].error = reinterpret_cast<my_bool*>(&error[i]);
219 }
220 }
221
235 static std::string getColumnsInError(std::vector<my_bools>& error,
236 const std::vector<std::string>& names) {
237 std::string result = "";
238
239 // Accumulate list of column names
240 for (size_t i = 0; i < names.size(); ++i) {
241 if (error[i] == MLM_TRUE) {
242 if (!result.empty()) {
243 result += ", ";
244 }
245 result += names[i];
246 }
247 }
248
249 if (result.empty()) {
250 result = "(None)";
251 }
252
253 return (result);
254 }
255
268 std::vector<MYSQL_BIND> createBindForSend(const HostPtr& host, const bool unique_ip) {
269 // Store host object to ensure it remains valid.
270 host_ = host;
271
272 // Initialize prior to constructing the array of MYSQL_BIND structures.
273 // It sets all fields, including is_null, to zero, so we need to set
274 // is_null only if it should be true. This gives up minor performance
275 // benefit while being safe approach.
276 memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
277
278 // Set up the structures for the various components of the host structure.
279
280 try {
281 // host_id : INT UNSIGNED NOT NULL
282 // The host_id is auto_incremented by MySQL database,
283 // so we need to pass the NULL value
284 host_id_ = 0;
285 bind_[0].buffer_type = MYSQL_TYPE_LONG;
286 bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
287 bind_[0].is_unsigned = MLM_TRUE;
288
289 // dhcp_identifier : VARBINARY(255) NOT NULL
290 dhcp_identifier_length_ = host->getIdentifier().size();
291 memcpy(static_cast<void*>(dhcp_identifier_buffer_),
292 &(host->getIdentifier())[0],
293 host->getIdentifier().size());
294
295 bind_[1].buffer_type = MYSQL_TYPE_BLOB;
296 bind_[1].buffer = dhcp_identifier_buffer_;
297 bind_[1].buffer_length = dhcp_identifier_length_;
298 bind_[1].length = &dhcp_identifier_length_;
299
300 // dhcp_identifier_type : TINYINT NOT NULL
301 dhcp_identifier_type_ = static_cast<uint8_t>(host->getIdentifierType());
302 bind_[2].buffer_type = MYSQL_TYPE_TINY;
303 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
304 bind_[2].is_unsigned = MLM_TRUE;
305
306 // dhcp4_subnet_id : INT UNSIGNED NULL
307 // Can't take an address of intermediate object, so let's store it
308 // in dhcp4_subnet_id_
309 dhcp4_subnet_id_ = host->getIPv4SubnetID();
310 dhcp4_subnet_id_null_ = host->getIPv4SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
311 bind_[3].buffer_type = MYSQL_TYPE_LONG;
312 bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
313 bind_[3].is_unsigned = MLM_TRUE;
314 bind_[3].is_null = &dhcp4_subnet_id_null_;
315
316 // dhcp6_subnet_id : INT UNSIGNED NULL
317 // Can't take an address of intermediate object, so let's store it
318 // in dhcp6_subnet_id_
319 dhcp6_subnet_id_ = host->getIPv6SubnetID();
320 dhcp6_subnet_id_null_ = host->getIPv6SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
321 bind_[4].buffer_type = MYSQL_TYPE_LONG;
322 bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
323 bind_[4].is_unsigned = MLM_TRUE;
324 bind_[4].is_null = &dhcp6_subnet_id_null_;
325
326 // ipv4_address : INT UNSIGNED NULL
327 // The address in the Host structure is an IOAddress object. Convert
328 // this to an integer for storage.
329 ipv4_address_ = host->getIPv4Reservation().toUint32();
330 ipv4_address_null_ = ipv4_address_ == 0 ? MLM_TRUE : MLM_FALSE;
331 bind_[5].buffer_type = MYSQL_TYPE_LONG;
332 bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
333 bind_[5].is_unsigned = MLM_TRUE;
334 bind_[5].is_null = &ipv4_address_null_;
335
336 // hostname : VARCHAR(255) NULL
337 strncpy(hostname_, host->getHostname().c_str(), HOSTNAME_MAX_LEN - 1);
338 hostname_length_ = host->getHostname().length();
339 bind_[6].buffer_type = MYSQL_TYPE_STRING;
340 bind_[6].buffer = reinterpret_cast<char*>(hostname_);
341 bind_[6].buffer_length = hostname_length_;
342
343 // dhcp4_client_classes : VARCHAR(255) NULL
344 bind_[7].buffer_type = MYSQL_TYPE_STRING;
345 // Override default separator to not include space after comma.
346 string classes4_txt = host->getClientClasses4().toText(",");
347 strncpy(dhcp4_client_classes_, classes4_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
348 bind_[7].buffer = dhcp4_client_classes_;
349 bind_[7].buffer_length = classes4_txt.length();
350
351 // dhcp6_client_classes : VARCHAR(255) NULL
352 bind_[8].buffer_type = MYSQL_TYPE_STRING;
353 // Override default separator to not include space after comma.
354 string classes6_txt = host->getClientClasses6().toText(",");
355 strncpy(dhcp6_client_classes_, classes6_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
356 bind_[8].buffer = dhcp6_client_classes_;
357 bind_[8].buffer_length = classes6_txt.length();
358
359 // user_context : TEXT NULL
360 ConstElementPtr ctx = host->getContext();
361 if (ctx) {
362 bind_[9].buffer_type = MYSQL_TYPE_STRING;
363 string ctx_txt = ctx->str();
364 strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
365 bind_[9].buffer = user_context_;
366 bind_[9].buffer_length = ctx_txt.length();
367 } else {
368 bind_[9].buffer_type = MYSQL_TYPE_NULL;
369 }
370
371 // ipv4_address : INT UNSIGNED NULL
372 // The address in the Host structure is an IOAddress object. Convert
373 // this to an integer for storage.
374 dhcp4_next_server_ = host->getNextServer().toUint32();
375 bind_[10].buffer_type = MYSQL_TYPE_LONG;
376 bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
377 bind_[10].is_unsigned = MLM_TRUE;
378 // bind_[10].is_null = &MLM_FALSE; // commented out for performance
379 // reasons, see memset() above
380
381 // dhcp4_server_hostname
382 bind_[11].buffer_type = MYSQL_TYPE_STRING;
383 std::string server_hostname = host->getServerHostname();
384 strncpy(dhcp4_server_hostname_, server_hostname.c_str(),
386 bind_[11].buffer = dhcp4_server_hostname_;
387 bind_[11].buffer_length = server_hostname.length();
388
389 // dhcp4_boot_file_name
390 bind_[12].buffer_type = MYSQL_TYPE_STRING;
391 std::string boot_file_name = host->getBootFileName();
392 strncpy(dhcp4_boot_file_name_, boot_file_name.c_str(),
394 bind_[12].buffer = dhcp4_boot_file_name_;
395 bind_[12].buffer_length = boot_file_name.length();
396
397 // auth key
398 bind_[13].buffer_type = MYSQL_TYPE_STRING;
399 std::string auth_key = host->getKey().toText();
400 std::strncpy(auth_key_, auth_key.c_str(), TEXT_AUTH_KEY_LEN - 1);
401 auth_key_null_ = auth_key.empty() ? MLM_TRUE : MLM_FALSE;
402 bind_[13].buffer = auth_key_;
403 bind_[13].buffer_length = auth_key.length();
404
405 } catch (const std::exception& ex) {
407 "Could not create bind array from Host: "
408 << host->getHostname() << ", reason: " << ex.what());
409 }
410
411 // Add the data to the vector.
412 std::vector<MYSQL_BIND> vec(bind_.begin(), bind_.begin() + HOST_COLUMNS);
413
414 // When checking whether the IP is unique we need to bind the IPv4 address
415 // at the end of the query as it has additional binding for the IPv4
416 // address.
417 if (unique_ip) {
418 vec.push_back(bind_[5]); // ipv4_address
419 vec.push_back(bind_[3]); // subnet_id
420 }
421 return (vec);
422 }
423
431 virtual std::vector<MYSQL_BIND> createBindForReceive() {
432 // Initialize MYSQL_BIND array.
433 // It sets all fields, including is_null, to zero, so we need to set
434 // is_null only if it should be true. This gives up minor performance
435 // benefit while being safe approach. For improved readability, the
436 // code that explicitly sets is_null is there, but is commented out.
437 // This also takes care of setting bind_[X].is_null to MLM_FALSE.
438 memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
439
440 // host_id : INT UNSIGNED NOT NULL
441 bind_[0].buffer_type = MYSQL_TYPE_LONG;
442 bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
443 bind_[0].is_unsigned = MLM_TRUE;
444
445 // dhcp_identifier : VARBINARY(255) NOT NULL
446 dhcp_identifier_length_ = sizeof(dhcp_identifier_buffer_);
447 bind_[1].buffer_type = MYSQL_TYPE_BLOB;
448 bind_[1].buffer = reinterpret_cast<char*>(dhcp_identifier_buffer_);
449 bind_[1].buffer_length = dhcp_identifier_length_;
450 bind_[1].length = &dhcp_identifier_length_;
451
452 // dhcp_identifier_type : TINYINT NOT NULL
453 bind_[2].buffer_type = MYSQL_TYPE_TINY;
454 bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
455 bind_[2].is_unsigned = MLM_TRUE;
456
457 // dhcp4_subnet_id : INT UNSIGNED NULL
458 dhcp4_subnet_id_null_ = MLM_FALSE;
459 bind_[3].buffer_type = MYSQL_TYPE_LONG;
460 bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
461 bind_[3].is_unsigned = MLM_TRUE;
462 bind_[3].is_null = &dhcp4_subnet_id_null_;
463
464 // dhcp6_subnet_id : INT UNSIGNED NULL
465 dhcp6_subnet_id_null_ = MLM_FALSE;
466 bind_[4].buffer_type = MYSQL_TYPE_LONG;
467 bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
468 bind_[4].is_unsigned = MLM_TRUE;
469 bind_[4].is_null = &dhcp6_subnet_id_null_;
470
471 // ipv4_address : INT UNSIGNED NULL
472 ipv4_address_null_ = MLM_FALSE;
473 bind_[5].buffer_type = MYSQL_TYPE_LONG;
474 bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
475 bind_[5].is_unsigned = MLM_TRUE;
476 bind_[5].is_null = &ipv4_address_null_;
477
478 // hostname : VARCHAR(255) NULL
479 hostname_null_ = MLM_FALSE;
480 hostname_length_ = sizeof(hostname_);
481 bind_[6].buffer_type = MYSQL_TYPE_STRING;
482 bind_[6].buffer = reinterpret_cast<char*>(hostname_);
483 bind_[6].buffer_length = hostname_length_;
484 bind_[6].length = &hostname_length_;
485 bind_[6].is_null = &hostname_null_;
486
487 // dhcp4_client_classes : VARCHAR(255) NULL
488 dhcp4_client_classes_null_ = MLM_FALSE;
489 dhcp4_client_classes_length_ = sizeof(dhcp4_client_classes_);
490 bind_[7].buffer_type = MYSQL_TYPE_STRING;
491 bind_[7].buffer = reinterpret_cast<char*>(dhcp4_client_classes_);
492 bind_[7].buffer_length = dhcp4_client_classes_length_;
493 bind_[7].length = &dhcp4_client_classes_length_;
494 bind_[7].is_null = &dhcp4_client_classes_null_;
495
496 // dhcp6_client_classes : VARCHAR(255) NULL
497 dhcp6_client_classes_null_ = MLM_FALSE;
498 dhcp6_client_classes_length_ = sizeof(dhcp6_client_classes_);
499 bind_[8].buffer_type = MYSQL_TYPE_STRING;
500 bind_[8].buffer = reinterpret_cast<char*>(dhcp6_client_classes_);
501 bind_[8].buffer_length = dhcp6_client_classes_length_;
502 bind_[8].length = &dhcp6_client_classes_length_;
503 bind_[8].is_null = &dhcp6_client_classes_null_;
504
505 // user_context : TEXT NULL
506 user_context_null_ = MLM_FALSE;
507 user_context_length_ = sizeof(user_context_);
508 bind_[9].buffer_type = MYSQL_TYPE_STRING;
509 bind_[9].buffer = reinterpret_cast<char*>(user_context_);
510 bind_[9].buffer_length = user_context_length_;
511 bind_[9].length = &user_context_length_;
512 bind_[9].is_null = &user_context_null_;
513
514 // dhcp4_next_server
515 dhcp4_next_server_null_ = MLM_FALSE;
516 bind_[10].buffer_type = MYSQL_TYPE_LONG;
517 bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
518 bind_[10].is_unsigned = MLM_TRUE;
519 bind_[10].is_null = &dhcp4_next_server_null_;
520
521 // dhcp4_server_hostname
522 dhcp4_server_hostname_null_ = MLM_FALSE;
523 dhcp4_server_hostname_length_ = sizeof(dhcp4_server_hostname_);
524 bind_[11].buffer_type = MYSQL_TYPE_STRING;
525 bind_[11].buffer = reinterpret_cast<char*>(dhcp4_server_hostname_);
526 bind_[11].buffer_length = dhcp4_server_hostname_length_;
527 bind_[11].length = &dhcp4_server_hostname_length_;
528 bind_[11].is_null = &dhcp4_server_hostname_null_;
529
530 // dhcp4_boot_file_name
531 dhcp4_boot_file_name_null_ = MLM_FALSE;
532 dhcp4_boot_file_name_length_ = sizeof(dhcp4_boot_file_name_);
533 bind_[12].buffer_type = MYSQL_TYPE_STRING;
534 bind_[12].buffer = reinterpret_cast<char*>(dhcp4_boot_file_name_);
535 bind_[12].buffer_length = dhcp4_boot_file_name_length_;
536 bind_[12].length = &dhcp4_boot_file_name_length_;
537 bind_[12].is_null = &dhcp4_boot_file_name_null_;
538
539 // auth_key_
540 auth_key_null_ = MLM_FALSE;
541 auth_key_length_ = sizeof(auth_key_);
542 bind_[13].buffer_type = MYSQL_TYPE_STRING;
543 bind_[13].buffer = reinterpret_cast<char*>(auth_key_);
544 bind_[13].buffer_length = auth_key_length_;
545 bind_[13].length = &auth_key_length_;
546 bind_[13].is_null = &auth_key_null_;
547
548 // Add the error flags
549 setErrorIndicators(bind_, error_);
550
551 // Add the data to the vector. Note the end element is one after the
552 // end of the array.
553 return (bind_);
554 }
555
564 HostPtr retrieveHost() {
565 // Check if the identifier stored in the database is correct.
566 if (dhcp_identifier_type_ > MAX_IDENTIFIER_TYPE) {
567 isc_throw(BadValue, "invalid dhcp identifier type returned: "
568 << static_cast<int>(dhcp_identifier_type_));
569 }
570 // Set the dhcp identifier type in a variable of the appropriate
571 // data type.
573 static_cast<Host::IdentifierType>(dhcp_identifier_type_);
574
575 // Set DHCPv4 subnet ID to the value returned. If NULL returned,
576 // set to 0.
577 SubnetID ipv4_subnet_id(SUBNET_ID_UNUSED);
578 if (dhcp4_subnet_id_null_ == MLM_FALSE) {
579 ipv4_subnet_id = dhcp4_subnet_id_;
580 }
581
582 // Set DHCPv6 subnet ID to the value returned. If NULL returned,
583 // set to 0.
584 SubnetID ipv6_subnet_id(SUBNET_ID_UNUSED);
585 if (dhcp6_subnet_id_null_ == MLM_FALSE) {
586 ipv6_subnet_id = dhcp6_subnet_id_;
587 }
588
589 // Set IPv4 address reservation if it was given, if not, set IPv4 zero
590 // address
592 if (ipv4_address_null_ == MLM_FALSE) {
593 ipv4_reservation = asiolink::IOAddress(ipv4_address_);
594 }
595
596 // Set hostname if non NULL value returned. Otherwise, leave an
597 // empty string.
598 std::string hostname;
599 if (hostname_null_ == MLM_FALSE) {
600 hostname = std::string(hostname_, hostname_length_);
601 }
602
603 // Set DHCPv4 client classes if non NULL value returned.
604 std::string dhcp4_client_classes;
605 if (dhcp4_client_classes_null_ == MLM_FALSE) {
606 dhcp4_client_classes = std::string(dhcp4_client_classes_,
607 dhcp4_client_classes_length_);
608 }
609
610 // Set DHCPv6 client classes if non NULL value returned.
611 std::string dhcp6_client_classes;
612 if (dhcp6_client_classes_null_ == MLM_FALSE) {
613 dhcp6_client_classes = std::string(dhcp6_client_classes_,
614 dhcp6_client_classes_length_);
615 }
616
617 // Convert user_context to string as well.
618 std::string user_context;
619 if (user_context_null_ == MLM_FALSE) {
620 user_context_[user_context_length_] = '\0';
621 user_context.assign(user_context_);
622 }
623
624 // Set next server value (siaddr) if non NULL value returned.
626 if (dhcp4_next_server_null_ == MLM_FALSE) {
627 next_server = asiolink::IOAddress(dhcp4_next_server_);
628 }
629
630 // Set server hostname (sname) if non NULL value returned.
631 std::string dhcp4_server_hostname;
632 if (dhcp4_server_hostname_null_ == MLM_FALSE) {
633 dhcp4_server_hostname = std::string(dhcp4_server_hostname_,
634 dhcp4_server_hostname_length_);
635 }
636
637 // Set boot file name (file) if non NULL value returned.
638 std::string dhcp4_boot_file_name;
639 if (dhcp4_boot_file_name_null_ == MLM_FALSE) {
640 dhcp4_boot_file_name = std::string(dhcp4_boot_file_name_,
641 dhcp4_boot_file_name_length_);
642 }
643
644 // Set the auth key if a non empty array is retrieved
645 std::string auth_key;
646 if (auth_key_null_ == MLM_FALSE) {
647 auth_key = std::string(auth_key_, auth_key_length_);
648 }
649
650 // Create and return Host object from the data gathered.
651 HostPtr h(new Host(dhcp_identifier_buffer_, dhcp_identifier_length_,
652 type, ipv4_subnet_id, ipv6_subnet_id, ipv4_reservation,
653 hostname, dhcp4_client_classes, dhcp6_client_classes,
654 next_server, dhcp4_server_hostname,
655 dhcp4_boot_file_name, AuthKey(auth_key)));
656 h->setHostId(host_id_);
657
658 // Set the user context if there is one.
659 if (!user_context.empty()) {
660 try {
661 ConstElementPtr ctx = Element::fromJSON(user_context);
662 if (!ctx || (ctx->getType() != Element::map)) {
663 isc_throw(BadValue, "user context '" << user_context
664 << "' is not a JSON map");
665 }
666 h->setContext(ctx);
667 } catch (const isc::data::JSONError& ex) {
668 isc_throw(BadValue, "user context '" << user_context
669 << "' is invalid JSON: " << ex.what());
670 }
671 }
672
673 return (h);
674 }
675
689 virtual void processFetchedData(ConstHostCollection& hosts) {
690 HostPtr host;
691 // Add new host only if there are no hosts yet or the host id of the
692 // most recently added host is different than the host id of the
693 // currently processed host.
694 if (hosts.empty() || (hosts.back()->getHostId() != getHostId())) {
695 // Create Host object from the fetched data and append it to the
696 // collection.
697 host = retrieveHost();
698 hosts.push_back(host);
699 }
700 }
701
712 std::string getErrorColumns() {
713 return (getColumnsInError(error_, columns_));
714 }
715
716protected:
717
719 size_t columns_num_;
720
722 std::vector<MYSQL_BIND> bind_;
723
725 std::vector<std::string> columns_;
726
728 std::vector<my_bools> error_;
729
732 HostPtr host_;
733
734private:
735
737 uint64_t host_id_;
738
741 uint8_t dhcp_identifier_buffer_[ClientId::MAX_CLIENT_ID_LEN];
742
744 unsigned long dhcp_identifier_length_;
745
748 uint8_t dhcp_identifier_type_;
749
751 uint32_t dhcp4_subnet_id_;
752
754 uint32_t dhcp6_subnet_id_;
755
757 uint32_t ipv4_address_;
758
760 char hostname_[HOSTNAME_MAX_LEN];
761
763 unsigned long hostname_length_;
764
766 char dhcp4_client_classes_[CLIENT_CLASSES_MAX_LEN];
767
770 unsigned long dhcp4_client_classes_length_;
771
773 char dhcp6_client_classes_[CLIENT_CLASSES_MAX_LEN];
774
777 unsigned long dhcp6_client_classes_length_;
778
780 char user_context_[USER_CONTEXT_MAX_LEN];
781
783 unsigned long user_context_length_;
784
786 uint32_t dhcp4_next_server_;
787
789 char dhcp4_server_hostname_[SERVER_HOSTNAME_MAX_LEN];
790
792 unsigned long dhcp4_server_hostname_length_;
793
795 char dhcp4_boot_file_name_[BOOT_FILE_NAME_MAX_LEN];
796
798 unsigned long dhcp4_boot_file_name_length_;
799
801 char auth_key_[TEXT_AUTH_KEY_LEN];
802
804 unsigned long auth_key_length_;
805
808
809
810 my_bool dhcp4_subnet_id_null_;
811
813 my_bool dhcp6_subnet_id_null_;
814
816 my_bool ipv4_address_null_;
817
819 my_bool hostname_null_;
820
823 my_bool dhcp4_client_classes_null_;
824
827 my_bool dhcp6_client_classes_null_;
828
830 my_bool user_context_null_;
831
833 my_bool dhcp4_next_server_null_;
834
836 my_bool dhcp4_server_hostname_null_;
837
839 my_bool dhcp4_boot_file_name_null_;
840
842 my_bool auth_key_null_;
843
845};
846
856class MySqlHostWithOptionsExchange : public MySqlHostExchange {
857private:
858
860 static const size_t OPTION_COLUMNS = 8;
861
876 class OptionProcessor {
877 public:
878
885 OptionProcessor(const Option::Universe& universe,
886 const size_t start_column)
887 : universe_(universe), start_column_(start_column), option_id_(0),
888 code_(0), value_length_(0), formatted_value_length_(0),
889 space_length_(0), persistent_(false), cancelled_(false),
890 user_context_length_(0),
891 option_id_null_(MLM_FALSE), code_null_(MLM_FALSE),
892 value_null_(MLM_FALSE), formatted_value_null_(MLM_FALSE),
893 space_null_(MLM_FALSE), user_context_null_(MLM_FALSE),
894 option_id_index_(start_column), code_index_(start_column_ + 1),
895 value_index_(start_column_ + 2),
896 formatted_value_index_(start_column_ + 3),
897 space_index_(start_column_ + 4),
898 persistent_index_(start_column_ + 5),
899 cancelled_index_(start_column_ + 6),
900 user_context_index_(start_column_ + 7),
901 most_recent_option_id_(0) {
902
903 memset(value_, 0, sizeof(value_));
904 memset(formatted_value_, 0, sizeof(formatted_value_));
905 memset(space_, 0, sizeof(space_));
906 memset(user_context_, 0, sizeof(user_context_));
907 }
908
910 uint64_t getOptionId() const {
911 if (option_id_null_ == MLM_FALSE) {
912 return (option_id_);
913 }
914 return (0);
915 }
916
929 void retrieveOption(const CfgOptionPtr& cfg) {
930 // option_id may be NULL if dhcp4_options or dhcp6_options table
931 // doesn't contain any options for the particular host. Also, the
932 // current option id must be greater than id if the most recent
933 // option because options are ordered by option id. Otherwise
934 // we assume that this is already processed option.
935 if ((option_id_null_ == MLM_TRUE) ||
936 (most_recent_option_id_ >= option_id_)) {
937 return;
938 }
939
940 // Remember current option id as the most recent processed one. We
941 // will be comparing it with option ids in subsequent rows.
942 most_recent_option_id_ = option_id_;
943
944 // Convert it to string object for easier comparison.
945 std::string space;
946 if (space_null_ == MLM_FALSE) {
947 // Typically, the string values returned by the database are not
948 // NULL terminated.
949 space_[space_length_] = '\0';
950 space.assign(space_);
951 }
952
953 // If empty or null space provided, use a default top level space.
954 if (space.empty()) {
955 space = (universe_ == Option::V4 ?
957 }
958
959 // Convert formatted_value to string as well.
960 std::string formatted_value;
961 if (formatted_value_null_ == MLM_FALSE) {
962 formatted_value_[formatted_value_length_] = '\0';
963 formatted_value.assign(formatted_value_);
964 }
965
966 // Convert user_context to string as well.
967 std::string user_context;
968 if (user_context_null_ == MLM_FALSE) {
969 user_context_[user_context_length_] = '\0';
970 user_context.assign(user_context_);
971 }
972
973 // Options are held in a binary or textual format in the database.
974 // This is similar to having an option specified in a server
975 // configuration file. Such option is converted to appropriate C++
976 // class, using option definition. Thus, we need to find the
977 // option definition for this option code and option space.
978
979 // Check if this is a standard option.
980 OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code_);
981
982 // Otherwise, we may check if this an option encapsulated within the
983 // vendor space.
984 if (!def && (space != DHCP4_OPTION_SPACE) &&
985 (space != DHCP6_OPTION_SPACE)) {
986 uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
987 if (vendor_id > 0) {
988 def = LibDHCP::getVendorOptionDef(universe_, vendor_id, code_);
989 }
990 }
991
992 // In all other cases, we use runtime option definitions, which
993 // should be also registered within the libdhcp++.
994 if (!def) {
995 def = LibDHCP::getRuntimeOptionDef(space, code_);
996 }
997
998 // Finish with a last resort option definition.
999 if (!def) {
1000 def = LibDHCP::getLastResortOptionDef(space, code_);
1001 }
1002
1003 OptionPtr option;
1004
1005 // If no definition found, we use generic option type.
1006 if (!def) {
1007 // We have to pay attention if the value is NULL. If it is,
1008 // we must create an empty option instance. We can't rely on
1009 // the value_length_ because it may contain garbage for the
1010 // null values. Thus we check explicitly whether or not the
1011 // NULL flag is set.
1012 if (value_null_ == MLM_FALSE) {
1013 OptionBuffer buf(value_, value_ + value_length_);
1014 option.reset(new Option(universe_, code_, buf.begin(),
1015 buf.end()));
1016 } else {
1017 option.reset(new Option(universe_, code_));
1018 }
1019 } else {
1020 // The option value may be specified in textual or binary format
1021 // in the database. If formatted_value is empty, the binary
1022 // format is used. Depending on the format we use a different
1023 // variant of the optionFactory function.
1024 if (formatted_value.empty()) {
1025 OptionBuffer buf(value_, value_ + value_length_);
1026 // Again, check if the value is null before submitting the
1027 // buffer to the factory function.
1028 option = def->optionFactory(universe_, code_, buf.begin(),
1029 value_null_ == MLM_FALSE ? buf.end() :
1030 buf.begin());
1031 } else {
1032 // Spit the value specified in comma separated values
1033 // format.
1034 std::vector<std::string> split_vec;
1035 boost::split(split_vec, formatted_value, boost::is_any_of(","));
1036 option = def->optionFactory(universe_, code_, split_vec);
1037 }
1038 }
1039
1040 OptionDescriptor desc(option, persistent_, cancelled_,
1041 formatted_value);
1042
1043 // Set the user context if there is one into the option descriptor.
1044 if (!user_context.empty()) {
1045 try {
1046 ConstElementPtr ctx = Element::fromJSON(user_context);
1047 if (!ctx || (ctx->getType() != Element::map)) {
1048 isc_throw(BadValue, "user context '" << user_context
1049 << "' is no a JSON map");
1050 }
1051 desc.setContext(ctx);
1052 } catch (const isc::data::JSONError& ex) {
1053 isc_throw(BadValue, "user context '" << user_context
1054 << "' is invalid JSON: " << ex.what());
1055 }
1056 }
1057 cfg->add(desc, space);
1058 }
1059
1064 void setColumnNames(std::vector<std::string>& columns) {
1065 columns[option_id_index_] = "option_id";
1066 columns[code_index_] = "code";
1067 columns[value_index_] = "value";
1068 columns[formatted_value_index_] = "formatted_value";
1069 columns[space_index_] = "space";
1070 columns[persistent_index_] = "persistent";
1071 columns[cancelled_index_] = "cancelled";
1072 columns[user_context_index_] = "user_context";
1073 }
1074
1081 void setBindFields(std::vector<MYSQL_BIND>& bind) {
1082 // This method is called just before making a new query, so we
1083 // reset the most_recent_option_id_ and other exchange members to
1084 // start over with options processing.
1085 most_recent_option_id_ = 0;
1086
1087 option_id_ = 0;
1088 code_ = 0;
1089 persistent_ = false;
1090 cancelled_ = false;
1091 option_id_null_ = MLM_FALSE;
1092 code_null_ = MLM_FALSE;
1093 value_null_ = MLM_FALSE;
1094 formatted_value_null_ = MLM_FALSE;
1095 space_null_ = MLM_FALSE;
1096 user_context_null_ = MLM_FALSE;
1097
1098 memset(value_, 0, sizeof(value_));
1099 memset(formatted_value_, 0, sizeof(formatted_value_));
1100 memset(space_, 0, sizeof(space_));
1101 memset(user_context_, 0, sizeof(user_context_));
1102
1103 // option_id : INT UNSIGNED NOT NULL AUTO_INCREMENT,
1104 bind[option_id_index_].buffer_type = MYSQL_TYPE_LONG;
1105 bind[option_id_index_].buffer = reinterpret_cast<char*>(&option_id_);
1106 bind[option_id_index_].is_unsigned = MLM_TRUE;
1107 bind[option_id_index_].is_null = &option_id_null_;
1108
1109 // code : TINYINT OR SHORT UNSIGNED NOT NULL
1110 bind[code_index_].buffer_type = MYSQL_TYPE_SHORT;
1111 bind[code_index_].buffer = reinterpret_cast<char*>(&code_);
1112 bind[code_index_].is_unsigned = MLM_TRUE;
1113 bind[code_index_].is_null = &code_null_;
1114
1115 // value : BLOB NULL
1116 value_length_ = sizeof(value_);
1117 bind[value_index_].buffer_type = MYSQL_TYPE_BLOB;
1118 bind[value_index_].buffer = reinterpret_cast<char*>(value_);
1119 bind[value_index_].buffer_length = value_length_;
1120 bind[value_index_].length = &value_length_;
1121 bind[value_index_].is_null = &value_null_;
1122
1123 // formatted_value : TEXT NULL
1124 formatted_value_length_ = sizeof(formatted_value_);
1125 bind[formatted_value_index_].buffer_type = MYSQL_TYPE_STRING;
1126 bind[formatted_value_index_].buffer = reinterpret_cast<char*>(formatted_value_);
1127 bind[formatted_value_index_].buffer_length = formatted_value_length_;
1128 bind[formatted_value_index_].length = &formatted_value_length_;
1129 bind[formatted_value_index_].is_null = &formatted_value_null_;
1130
1131 // space : VARCHAR(128) NULL
1132 space_length_ = sizeof(space_);
1133 bind[space_index_].buffer_type = MYSQL_TYPE_STRING;
1134 bind[space_index_].buffer = reinterpret_cast<char*>(space_);
1135 bind[space_index_].buffer_length = space_length_;
1136 bind[space_index_].length = &space_length_;
1137 bind[space_index_].is_null = &space_null_;
1138
1139 // persistent : TINYINT(1) NOT NULL DEFAULT 0
1140 bind[persistent_index_].buffer_type = MYSQL_TYPE_TINY;
1141 bind[persistent_index_].buffer = reinterpret_cast<char*>(&persistent_);
1142 bind[persistent_index_].is_unsigned = MLM_TRUE;
1143
1144 // cancelled : TINYINT(1) NOT NULL DEFAULT 0
1145 bind[cancelled_index_].buffer_type = MYSQL_TYPE_TINY;
1146 bind[cancelled_index_].buffer = reinterpret_cast<char*>(&cancelled_);
1147 bind[cancelled_index_].is_unsigned = MLM_TRUE;
1148
1149 // user_context : TEXT NULL
1150 user_context_length_ = sizeof(user_context_);
1151 bind[user_context_index_].buffer_type = MYSQL_TYPE_STRING;
1152 bind[user_context_index_].buffer = reinterpret_cast<char*>(user_context_);
1153 bind[user_context_index_].buffer_length = user_context_length_;
1154 bind[user_context_index_].length = &user_context_length_;
1155 bind[user_context_index_].is_null = &user_context_null_;
1156 }
1157
1158 private:
1159
1161 Option::Universe universe_;
1162
1164 size_t start_column_;
1165
1167 uint32_t option_id_;
1168
1170 uint16_t code_;
1171
1173 uint8_t value_[OPTION_VALUE_MAX_LEN];
1174
1176 unsigned long value_length_;
1177
1179 char formatted_value_[OPTION_FORMATTED_VALUE_MAX_LEN];
1180
1182 unsigned long formatted_value_length_;
1183
1185 char space_[OPTION_SPACE_MAX_LEN];
1186
1188 unsigned long space_length_;
1189
1192 bool persistent_;
1193
1195 bool cancelled_;
1196
1198 char user_context_[USER_CONTEXT_MAX_LEN];
1199
1201 unsigned long user_context_length_;
1202
1205
1206
1207 my_bool option_id_null_;
1208
1210 my_bool code_null_;
1211
1213 my_bool value_null_;
1214
1217 my_bool formatted_value_null_;
1218
1220 my_bool space_null_;
1221
1223 my_bool user_context_null_;
1225
1227
1228
1229 size_t option_id_index_;
1230
1232 size_t code_index_;
1233
1235 size_t value_index_;
1236
1238 size_t formatted_value_index_;
1239
1241 size_t space_index_;
1242
1244 size_t persistent_index_;
1245
1247 size_t cancelled_index_;
1249
1251 size_t user_context_index_;
1252
1254 uint32_t most_recent_option_id_;
1255 };
1256
1258 typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
1259
1260public:
1261
1268 enum FetchedOptions {
1269 DHCP4_ONLY,
1270 DHCP6_ONLY,
1271 DHCP4_AND_DHCP6
1272 };
1273
1282 MySqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
1283 const size_t additional_columns_num = 0)
1284 : MySqlHostExchange(getRequiredColumnsNum(fetched_options)
1285 + additional_columns_num),
1286 opt_proc4_(), opt_proc6_() {
1287
1288 // Create option processor for DHCPv4 options, if required.
1289 if ((fetched_options == DHCP4_ONLY) ||
1290 (fetched_options == DHCP4_AND_DHCP6)) {
1291 opt_proc4_.reset(new OptionProcessor(Option::V4,
1292 findAvailColumn()));
1293 opt_proc4_->setColumnNames(columns_);
1294 }
1295
1296 // Create option processor for DHCPv6 options, if required.
1297 if ((fetched_options == DHCP6_ONLY) ||
1298 (fetched_options == DHCP4_AND_DHCP6)) {
1299 opt_proc6_.reset(new OptionProcessor(Option::V6,
1300 findAvailColumn()));
1301 opt_proc6_->setColumnNames(columns_);
1302 }
1303 }
1304
1313 virtual void processFetchedData(ConstHostCollection& hosts) override {
1314 // Holds pointer to the previously parsed host.
1315 HostPtr most_recent_host;
1316 if (!hosts.empty()) {
1317 // Const cast is not very elegant way to deal with it, but
1318 // there is a good reason to use it here. This method is called
1319 // to build a collection of const hosts to be returned to the
1320 // caller. If we wanted to use non-const collection we'd need
1321 // to copy the whole collection before returning it, which has
1322 // performance implications. Alternatively, we could store the
1323 // most recently added host in a class member but this would
1324 // make the code less readable.
1325 most_recent_host = boost::const_pointer_cast<Host>(hosts.back());
1326 }
1327
1328 // If no host has been parsed yet or we're at the row holding next
1329 // host, we create a new host object and put it at the end of the
1330 // list.
1331 if (!most_recent_host || (most_recent_host->getHostId() < getHostId())) {
1332 HostPtr host = retrieveHost();
1333 hosts.push_back(host);
1334 most_recent_host = host;
1335 }
1336
1337 // Parse DHCPv4 options if required to do so.
1338 if (opt_proc4_) {
1339 CfgOptionPtr cfg = most_recent_host->getCfgOption4();
1340 opt_proc4_->retrieveOption(cfg);
1341 }
1342
1343 // Parse DHCPv6 options if required to do so.
1344 if (opt_proc6_) {
1345 CfgOptionPtr cfg = most_recent_host->getCfgOption6();
1346 opt_proc6_->retrieveOption(cfg);
1347 }
1348 }
1349
1353 virtual std::vector<MYSQL_BIND> createBindForReceive() override {
1354 // The following call sets bind_ values between 0 and 8.
1355 static_cast<void>(MySqlHostExchange::createBindForReceive());
1356
1357 // Bind variables for DHCPv4 options.
1358 if (opt_proc4_) {
1359 opt_proc4_->setBindFields(bind_);
1360 }
1361
1362 // Bind variables for DHCPv6 options.
1363 if (opt_proc6_) {
1364 opt_proc6_->setBindFields(bind_);
1365 }
1366
1367 // Add the error flags
1368 setErrorIndicators(bind_, error_);
1369
1370 return (bind_);
1371 }
1372
1373private:
1374
1386 static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
1387 return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
1388 OPTION_COLUMNS);
1389 }
1390
1394 OptionProcessorPtr opt_proc4_;
1395
1399 OptionProcessorPtr opt_proc6_;
1400};
1401
1414class MySqlHostIPv6Exchange : public MySqlHostWithOptionsExchange {
1415private:
1416
1418 static const size_t RESERVATION_COLUMNS = 5;
1419
1420public:
1421
1426 MySqlHostIPv6Exchange(const FetchedOptions& fetched_options)
1427 : MySqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
1428 reservation_id_(0),
1429 reserv_type_(0), reserv_type_null_(MLM_FALSE),
1430 ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0),
1431 reservation_id_index_(findAvailColumn()),
1432 address_index_(reservation_id_index_ + 1),
1433 prefix_len_index_(reservation_id_index_ + 2),
1434 type_index_(reservation_id_index_ + 3),
1435 iaid_index_(reservation_id_index_ + 4),
1436 most_recent_reservation_id_(0) {
1437
1438 memset(ipv6_address_buffer_, 0, sizeof(ipv6_address_buffer_));
1439
1440 // Provide names of additional columns returned by the queries.
1441 columns_[reservation_id_index_] = "reservation_id";
1442 columns_[address_index_] = "address";
1443 columns_[prefix_len_index_] = "prefix_len";
1444 columns_[type_index_] = "type";
1445 columns_[iaid_index_] = "dhcp6_iaid";
1446 }
1447
1451 uint32_t getReservationId() const {
1452 if (reserv_type_null_ == MLM_FALSE) {
1453 return (reservation_id_);
1454 }
1455 return (0);
1456 }
1457
1464 IPv6Resrv retrieveReservation() {
1465 // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
1467
1468 switch (reserv_type_) {
1469 case 0:
1470 type = IPv6Resrv::TYPE_NA;
1471 break;
1472
1473 case 2:
1474 type = IPv6Resrv::TYPE_PD;
1475 break;
1476
1477 default:
1479 "invalid IPv6 reservation type returned: "
1480 << static_cast<int>(reserv_type_)
1481 << ". Only 0 or 2 are allowed.");
1482 }
1483
1484 IOAddress addr6 = IOAddress::fromBytes(AF_INET6, ipv6_address_buffer_);
1485 IPv6Resrv r(type, addr6, prefix_len_);
1486 return (r);
1487 }
1488
1508 virtual void processFetchedData(ConstHostCollection& hosts) override {
1509
1510 // Call parent class to fetch host information and options.
1511 MySqlHostWithOptionsExchange::processFetchedData(hosts);
1512
1513 if (getReservationId() == 0) {
1514 return;
1515 }
1516
1517 if (hosts.empty()) {
1518 isc_throw(Unexpected, "no host information while retrieving"
1519 " IPv6 reservation");
1520 }
1521 HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
1522
1523 // If we're dealing with a new reservation, let's add it to the
1524 // host.
1525 if (getReservationId() > most_recent_reservation_id_) {
1526 most_recent_reservation_id_ = getReservationId();
1527
1528 if (most_recent_reservation_id_ > 0) {
1529 host->addReservation(retrieveReservation());
1530 }
1531 }
1532 }
1533
1542 virtual std::vector<MYSQL_BIND> createBindForReceive() override {
1543 // Reset most recent reservation id value because we're now making
1544 // a new SELECT query.
1545 most_recent_reservation_id_ = 0;
1546
1547 // Bind values supported by parent classes.
1548 static_cast<void>(MySqlHostWithOptionsExchange::createBindForReceive());
1549
1550 // reservation_id : INT UNSIGNED NOT NULL AUTO_INCREMENT
1551 bind_[reservation_id_index_].buffer_type = MYSQL_TYPE_LONG;
1552 bind_[reservation_id_index_].buffer = reinterpret_cast<char*>(&reservation_id_);
1553 bind_[reservation_id_index_].is_unsigned = MLM_TRUE;
1554
1555 // IPv6 address/prefix BINARY(16)
1556 ipv6_address_buffer_len_ = isc::asiolink::V6ADDRESS_LEN;
1557 bind_[address_index_].buffer_type = MYSQL_TYPE_BLOB;
1558 bind_[address_index_].buffer = reinterpret_cast<char*>(ipv6_address_buffer_);
1559 bind_[address_index_].buffer_length = ipv6_address_buffer_len_;
1560 bind_[address_index_].length = &ipv6_address_buffer_len_;
1561
1562 // prefix_len : TINYINT
1563 bind_[prefix_len_index_].buffer_type = MYSQL_TYPE_TINY;
1564 bind_[prefix_len_index_].buffer = reinterpret_cast<char*>(&prefix_len_);
1565 bind_[prefix_len_index_].is_unsigned = MLM_TRUE;
1566
1567 // (reservation) type : TINYINT
1568 reserv_type_null_ = MLM_FALSE;
1569 bind_[type_index_].buffer_type = MYSQL_TYPE_TINY;
1570 bind_[type_index_].buffer = reinterpret_cast<char*>(&reserv_type_);
1571 bind_[type_index_].is_unsigned = MLM_TRUE;
1572 bind_[type_index_].is_null = &reserv_type_null_;
1573
1574 // dhcp6_iaid INT UNSIGNED
1575 bind_[iaid_index_].buffer_type = MYSQL_TYPE_LONG;
1576 bind_[iaid_index_].buffer = reinterpret_cast<char*>(&iaid_);
1577 bind_[iaid_index_].is_unsigned = MLM_TRUE;
1578
1579 // Add the error flags
1580 setErrorIndicators(bind_, error_);
1581
1582 return (bind_);
1583 }
1584
1585private:
1586
1588 uint32_t reservation_id_;
1589
1591 uint8_t reserv_type_;
1592
1597 my_bool reserv_type_null_;
1598
1600 uint8_t ipv6_address_buffer_[isc::asiolink::V6ADDRESS_LEN];
1601
1603 unsigned long ipv6_address_buffer_len_;
1604
1606 uint8_t prefix_len_;
1607
1609 uint32_t iaid_;
1610
1612
1613
1614 size_t reservation_id_index_;
1615
1617 size_t address_index_;
1618
1620 size_t prefix_len_index_;
1621
1623 size_t type_index_;
1624
1626 size_t iaid_index_;
1627
1629
1631 uint32_t most_recent_reservation_id_;
1632};
1633
1644class MySqlIPv6ReservationExchange {
1645private:
1646
1648 static const size_t RESRV_COLUMNS = 6;
1649
1650public:
1651
1655 MySqlIPv6ReservationExchange()
1656 : host_id_(0), prefix_len_(0), type_(0),
1657 iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
1658
1659 // Reset error table.
1660 std::fill(&error_[0], &error_[RESRV_COLUMNS], MLM_FALSE);
1661
1662 // Set the column names (for error messages)
1663 columns_[0] = "host_id";
1664 columns_[1] = "address";
1665 columns_[2] = "prefix_len";
1666 columns_[3] = "type";
1667 columns_[4] = "dhcp6_iaid";
1668
1669 BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
1670 }
1671
1686 std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv,
1687 const HostID& id,
1688 const bool unique_ip) {
1689
1690 // Store the values to ensure they remain valid.
1691 resv_ = resv;
1692 host_id_ = id;
1693
1694 // Initialize prior to constructing the array of MYSQL_BIND structures.
1695 // It sets all fields, including is_null, to zero, so we need to set
1696 // is_null only if it should be true. This gives up minor performance
1697 // benefit while being safe approach. For improved readability, the
1698 // code that explicitly sets is_null is there, but is commented out.
1699 memset(bind_, 0, sizeof(bind_));
1700
1701 // Set up the structures for the various components of the host structure.
1702
1703 try {
1704 addr6_ = resv.getPrefix().toBytes();
1705 if (addr6_.size() != isc::asiolink::V6ADDRESS_LEN) {
1706 isc_throw(DbOperationError, "createBindForSend() - prefix is not "
1707 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
1708 }
1709
1710 addr6_length_ = isc::asiolink::V6ADDRESS_LEN;
1711 bind_[0].buffer_type = MYSQL_TYPE_BLOB;
1712 bind_[0].buffer = reinterpret_cast<char*>(&addr6_[0]);
1713 bind_[0].buffer_length = isc::asiolink::V6ADDRESS_LEN;
1714 bind_[0].length = &addr6_length_;
1715
1716 // prefix_len tinyint
1717 prefix_len_ = resv.getPrefixLen();
1718 bind_[1].buffer_type = MYSQL_TYPE_TINY;
1719 bind_[1].buffer = reinterpret_cast<char*>(&prefix_len_);
1720 bind_[1].is_unsigned = MLM_TRUE;
1721
1722 // type tinyint
1723 // See lease6_types for values (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
1724 type_ = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1725 bind_[2].buffer_type = MYSQL_TYPE_TINY;
1726 bind_[2].buffer = reinterpret_cast<char*>(&type_);
1727 bind_[2].is_unsigned = MLM_TRUE;
1728
1729 // dhcp6_iaid INT UNSIGNED
1731 iaid_ = 0;
1732 bind_[3].buffer_type = MYSQL_TYPE_LONG;
1733 bind_[3].buffer = reinterpret_cast<char*>(&iaid_);
1734 bind_[3].is_unsigned = MLM_TRUE;
1735
1736 // host_id INT UNSIGNED NOT NULL
1737 bind_[4].buffer_type = MYSQL_TYPE_LONG;
1738 bind_[4].buffer = reinterpret_cast<char*>(&host_id_);
1739 bind_[4].is_unsigned = MLM_TRUE;
1740
1741 } catch (const std::exception& ex) {
1743 "Could not create bind array from IPv6 Reservation: "
1744 << resv_.toText() << ", reason: " << ex.what());
1745 }
1746
1747 // Add the data to the vector. Note the end element is one after the
1748 // end of the array.
1749 // RESRV_COLUMNS -1 as we do not set reservation_id.
1750 std::vector<MYSQL_BIND> vec(&bind_[0], &bind_[RESRV_COLUMNS-1]);
1751
1752 // When checking whether the IP is unique we need to bind the IPv6 address
1753 // and prefix length at the end of the query as it has additional binding
1754 // for the IPv6 address and prefix length.
1755 if (unique_ip) {
1756 vec.push_back(bind_[0]); // address
1757 vec.push_back(bind_[1]); // prefix_len
1758 }
1759
1760 return (vec);
1761 }
1762
1763private:
1764
1766 uint64_t host_id_;
1767
1769 uint8_t prefix_len_;
1770
1772 uint8_t type_;
1773
1775 uint8_t iaid_;
1776
1778 IPv6Resrv resv_;
1779
1781 MYSQL_BIND bind_[RESRV_COLUMNS];
1782
1784 std::string columns_[RESRV_COLUMNS];
1785
1788 my_bool error_[RESRV_COLUMNS];
1789
1791 std::vector<uint8_t> addr6_;
1792
1794 unsigned long addr6_length_;
1795};
1796
1800class MySqlOptionExchange {
1802 static const size_t OPTION_COLUMNS = 10;
1803
1804public:
1805
1807 MySqlOptionExchange()
1808 : type_(0), value_len_(0), formatted_value_len_(0), space_(),
1809 space_len_(0), persistent_(false), cancelled_(false),
1810 user_context_(), user_context_len_(0), subnet_id_(SUBNET_ID_UNUSED),
1811 host_id_(0), option_() {
1812
1813 BOOST_STATIC_ASSERT(10 <= OPTION_COLUMNS);
1814 }
1815
1819 std::vector<MYSQL_BIND> createBindForSend(const OptionDescriptor& opt_desc,
1820 const std::string& opt_space,
1821 const Optional<SubnetID>& subnet_id,
1822 const HostID& host_id) {
1823
1824 // Hold pointer to the option to make sure it remains valid until
1825 // we complete a query.
1826 option_ = opt_desc.option_;
1827
1828 memset(bind_, 0, sizeof(bind_));
1829
1830 try {
1831 // option_id: INT UNSIGNED NOT NULL
1832 // The option_id is auto_incremented, so we need to pass the NULL
1833 // value.
1834 bind_[0].buffer_type = MYSQL_TYPE_NULL;
1835
1836 // code: SMALLINT UNSIGNED NOT NULL
1837 type_ = option_->getType();
1838 bind_[1].buffer_type = MYSQL_TYPE_SHORT;
1839 bind_[1].buffer = reinterpret_cast<char*>(&type_);
1840 bind_[1].is_unsigned = MLM_TRUE;
1841
1842 // value: BLOB NULL
1843 if (opt_desc.formatted_value_.empty() &&
1844 (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
1845 // The formatted_value is empty and the option value is
1846 // non-empty so we need to prepare on-wire format for the
1847 // option and store it in the database as a blob.
1848 OutputBuffer buf(opt_desc.option_->len());
1849 opt_desc.option_->pack(buf);
1850 const uint8_t* buf_ptr = buf.getData();
1851 value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
1852 buf_ptr + buf.getLength());
1853 value_len_ = value_.size();
1854 bind_[2].buffer_type = MYSQL_TYPE_BLOB;
1855 bind_[2].buffer = &value_[0];
1856 bind_[2].buffer_length = value_len_;
1857 bind_[2].length = &value_len_;
1858
1859 } else {
1860 // No value or formatted_value specified. In this case, the
1861 // value blob is NULL.
1862 value_.clear();
1863 bind_[2].buffer_type = MYSQL_TYPE_NULL;
1864 }
1865
1866 // formatted_value: TEXT NULL,
1867 if (!opt_desc.formatted_value_.empty()) {
1868 formatted_value_len_ = opt_desc.formatted_value_.size();
1869 bind_[3].buffer_type = MYSQL_TYPE_STRING;
1870 bind_[3].buffer = const_cast<char*>(opt_desc.formatted_value_.c_str());
1871 bind_[3].buffer_length = formatted_value_len_;
1872 bind_[3].length = &formatted_value_len_;
1873
1874 } else {
1875 bind_[3].buffer_type = MYSQL_TYPE_NULL;
1876 }
1877
1878 // space: VARCHAR(128) NULL
1879 space_ = opt_space;
1880 space_len_ = space_.size();
1881 bind_[4].buffer_type = MYSQL_TYPE_STRING;
1882 bind_[4].buffer = const_cast<char*>(space_.c_str());
1883 bind_[4].buffer_length = space_len_;
1884 bind_[4].length = &space_len_;
1885
1886 // persistent: TINYINT(1) NOT NULL DEFAULT 0
1887 persistent_ = opt_desc.persistent_;
1888 bind_[5].buffer_type = MYSQL_TYPE_TINY;
1889 bind_[5].buffer = reinterpret_cast<char*>(&persistent_);
1890 bind_[5].is_unsigned = MLM_TRUE;
1891
1892 // cancelled: TINYINT(1) NOT NULL DEFAULT 0
1893 cancelled_ = opt_desc.cancelled_;
1894 bind_[6].buffer_type = MYSQL_TYPE_TINY;
1895 bind_[6].buffer = reinterpret_cast<char*>(&cancelled_);
1896 bind_[6].is_unsigned = MLM_TRUE;
1897
1898 // user_context: TEST NULL,
1899 ConstElementPtr ctx = opt_desc.getContext();
1900 if (ctx) {
1901 user_context_ = ctx->str();
1902 user_context_len_ = user_context_.size();
1903 bind_[7].buffer_type = MYSQL_TYPE_STRING;
1904 bind_[7].buffer = const_cast<char*>(user_context_.c_str());
1905 bind_[7].buffer_length = user_context_len_;
1906 bind_[7].length = &user_context_len_;
1907 } else {
1908 bind_[7].buffer_type = MYSQL_TYPE_NULL;
1909 }
1910
1911 // dhcp4_subnet_id: INT UNSIGNED NULL
1912 if (!subnet_id.unspecified()) {
1913 subnet_id_ = subnet_id;
1914 bind_[8].buffer_type = MYSQL_TYPE_LONG;
1915 bind_[8].buffer = reinterpret_cast<char*>(subnet_id_);
1916 bind_[8].is_unsigned = MLM_TRUE;
1917
1918 } else {
1919 bind_[8].buffer_type = MYSQL_TYPE_NULL;
1920 }
1921
1922 // host_id: INT UNSIGNED NOT NULL
1923 host_id_ = host_id;
1924 bind_[9].buffer_type = MYSQL_TYPE_LONG;
1925 bind_[9].buffer = reinterpret_cast<char*>(&host_id_);
1926 bind_[9].is_unsigned = MLM_TRUE;
1927
1928 } catch (const std::exception& ex) {
1930 "Could not create bind array for inserting DHCP "
1931 "option: " << option_->toText() << ", reason: "
1932 << ex.what());
1933 }
1934
1935 return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[OPTION_COLUMNS]));
1936 }
1937
1938private:
1939
1941 uint16_t type_;
1942
1944 std::vector<uint8_t> value_;
1945
1947 unsigned long value_len_;
1948
1950 unsigned long formatted_value_len_;
1951
1953 std::string space_;
1954
1956 unsigned long space_len_;
1957
1960 bool persistent_;
1961
1964 bool cancelled_;
1965
1967 std::string user_context_;
1968
1970 unsigned long user_context_len_;
1971
1973 uint32_t subnet_id_;
1974
1976 uint32_t host_id_;
1977
1979 OptionPtr option_;
1980
1982 MYSQL_BIND bind_[OPTION_COLUMNS];
1983};
1984
1985} // namespace
1986
1987namespace isc {
1988namespace dhcp {
1989
2000public:
2001
2008 IOServiceAccessorPtr io_service_accessor,
2009 db::DbCallback db_reconnect_callback);
2010
2015
2018 boost::shared_ptr<MySqlHostWithOptionsExchange> host_ipv4_exchange_;
2019
2022 boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv6_exchange_;
2023
2027 boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv46_exchange_;
2028
2031 boost::shared_ptr<MySqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
2032
2036 boost::shared_ptr<MySqlOptionExchange> host_option_exchange_;
2037
2040
2043};
2044
2052public:
2053
2055 std::vector<MySqlHostContextPtr> pool_;
2056
2058 std::mutex mutex_;
2059};
2060
2062typedef boost::shared_ptr<MySqlHostContextPool> MySqlHostContextPoolPtr;
2063
2066public:
2067
2077 GET_HOST_DHCPID, // Gets hosts by host identifier
2078 GET_HOST_ADDR, // Gets hosts by IPv4 address
2079 GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
2080 GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
2081 GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
2082 GET_HOST_PREFIX, // Gets host by IPv6 prefix
2083 GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
2084 GET_HOST_ADDR6, // Gets hosts by IPv6 address/prefix
2085 GET_HOST_SUBID4, // Gets hosts by IPv4 SubnetID
2086 GET_HOST_SUBID6, // Gets hosts by IPv6 SubnetID
2087 GET_HOST_HOSTNAME, // Gets hosts by hostname
2088 GET_HOST_HOSTNAME_SUBID4, // Gets hosts by hostname and IPv4 SubnetID
2089 GET_HOST_HOSTNAME_SUBID6, // Gets hosts by hostname and IPv6 SubnetID
2090 GET_HOST_SUBID4_PAGE, // Gets hosts by IPv4 SubnetID beginning by HID
2091 GET_HOST_SUBID6_PAGE, // Gets hosts by IPv6 SubnetID beginning by HID
2092 GET_HOST_PAGE4, // Gets v4 hosts beginning by HID
2093 GET_HOST_PAGE6, // Gets v6 hosts beginning by HID
2094 INSERT_HOST_NON_UNIQUE_IP, // Insert new host to collection with allowing IP duplicates
2095 INSERT_HOST_UNIQUE_IP, // Insert new host to collection with checking for IP duplicates
2096 INSERT_V6_RESRV_NON_UNIQUE,// Insert v6 reservation without checking that it is unique
2097 INSERT_V6_RESRV_UNIQUE, // Insert v6 reservation with checking that it is unique
2098 INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
2099 INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
2100 DEL_HOST_ADDR4, // Delete v4 host (subnet-id, addr4)
2101 DEL_HOST_ADDR6, // Delete v6 host (subnet-id, addr6)
2102 DEL_HOST_SUBID4_ID, // Delete v4 host (subnet-id, ident.type, identifier)
2103 DEL_HOST_SUBID6_ID, // Delete v6 host (subnet-id, ident.type, identifier)
2104 NUM_STATEMENTS // Number of statements
2106
2113
2119
2122
2145 static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl);
2146
2157
2170 std::pair<uint32_t, uint32_t> getVersion(const std::string& timer_name = std::string()) const;
2171
2182 std::vector<MYSQL_BIND>& bind);
2183
2193 StatementIndex stindex,
2194 MYSQL_BIND* bind);
2195
2201 void addResv(MySqlHostContextPtr& ctx,
2202 const IPv6Resrv& resv,
2203 const HostID& id);
2204
2216 const OptionDescriptor& opt_desc,
2217 const std::string& opt_space,
2218 const Optional<SubnetID>& subnet_id,
2219 const HostID& host_id);
2220
2229 const StatementIndex& stindex,
2230 const ConstCfgOptionPtr& options_cfg,
2231 const uint64_t host_id);
2232
2245 const int status,
2246 const StatementIndex index,
2247 const char* what) const;
2248
2268 StatementIndex stindex,
2269 MYSQL_BIND* bind,
2270 boost::shared_ptr<MySqlHostExchange> exchange,
2271 ConstHostCollection& result,
2272 bool single) const;
2273
2292 const SubnetID& subnet_id,
2293 const Host::IdentifierType& identifier_type,
2294 const uint8_t* identifier_begin,
2295 const size_t identifier_len,
2296 StatementIndex stindex,
2297 boost::shared_ptr<MySqlHostExchange> exchange) const;
2298
2308 void checkReadOnly(MySqlHostContextPtr& ctx) const;
2309
2312
2316
2319
2323
2325 std::string timer_name_;
2326};
2327
2328namespace {
2329
2331typedef boost::array<TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS>
2332TaggedStatementArray;
2333
2336TaggedStatementArray tagged_statements = { {
2337 // Retrieves host information, IPv6 reservations and both DHCPv4 and
2338 // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2339 // to retrieve information from 4 different tables using a single query.
2340 // Hence, this query returns multiple rows for a single host.
2342 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2343 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2344 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2345 "h.user_context, "
2346 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2347 "h.dhcp4_boot_file_name, h.auth_key, "
2348 "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2349 "o4.persistent, o4.cancelled, o4.user_context, "
2350 "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2351 "o6.persistent, o6.cancelled, o6.user_context, "
2352 "r.reservation_id, r.address, r.prefix_len, r.type, "
2353 "r.dhcp6_iaid "
2354 "FROM hosts AS h "
2355 "LEFT JOIN dhcp4_options AS o4 "
2356 "ON h.host_id = o4.host_id "
2357 "LEFT JOIN dhcp6_options AS o6 "
2358 "ON h.host_id = o6.host_id "
2359 "LEFT JOIN ipv6_reservations AS r "
2360 "ON h.host_id = r.host_id "
2361 "WHERE dhcp_identifier = ? AND dhcp_identifier_type = ? "
2362 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2363
2364 // Retrieves host information along with the DHCPv4 options associated with
2365 // it. Left joining the dhcp4_options table results in multiple rows being
2366 // returned for the same host. The host is retrieved by IPv4 address.
2368 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2369 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2370 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2371 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2372 "h.dhcp4_boot_file_name, h.auth_key, "
2373 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2374 "o.persistent, o.cancelled, o.user_context "
2375 "FROM hosts AS h "
2376 "LEFT JOIN dhcp4_options AS o "
2377 "ON h.host_id = o.host_id "
2378 "WHERE ipv4_address = ? "
2379 "ORDER BY h.host_id, o.option_id"},
2380
2381 // Retrieves host information and DHCPv4 options using subnet identifier
2382 // and client's identifier. Left joining the dhcp4_options table results in
2383 // multiple rows being returned for the same host.
2385 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2386 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2387 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2388 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2389 "h.dhcp4_boot_file_name, h.auth_key, "
2390 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2391 "o.persistent, o.cancelled, o.user_context "
2392 "FROM hosts AS h "
2393 "LEFT JOIN dhcp4_options AS o "
2394 "ON h.host_id = o.host_id "
2395 "WHERE h.dhcp4_subnet_id = ? AND h.dhcp_identifier_type = ? "
2396 "AND h.dhcp_identifier = ? "
2397 "ORDER BY h.host_id, o.option_id"},
2398
2399 // Retrieves host information, IPv6 reservations and DHCPv6 options
2400 // associated with a host. The number of rows returned is a multiplication
2401 // of number of IPv6 reservations and DHCPv6 options.
2403 "SELECT h.host_id, h.dhcp_identifier, "
2404 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2405 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2406 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2407 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2408 "h.dhcp4_boot_file_name, h.auth_key, "
2409 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2410 "o.persistent, o.cancelled, o.user_context, "
2411 "r.reservation_id, r.address, r.prefix_len, r.type, "
2412 "r.dhcp6_iaid "
2413 "FROM hosts AS h "
2414 "LEFT JOIN dhcp6_options AS o "
2415 "ON h.host_id = o.host_id "
2416 "LEFT JOIN ipv6_reservations AS r "
2417 "ON h.host_id = r.host_id "
2418 "WHERE h.dhcp6_subnet_id = ? AND h.dhcp_identifier_type = ? "
2419 "AND h.dhcp_identifier = ? "
2420 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2421
2422 // Retrieves host information and DHCPv4 options for the host using subnet
2423 // identifier and IPv4 reservation. Left joining the dhcp4_options table
2424 // results in multiple rows being returned for the host. The number of
2425 // rows depends on the number of options defined for the host.
2427 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2428 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2429 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2430 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2431 "h.dhcp4_boot_file_name, h.auth_key, "
2432 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2433 "o.persistent, o.cancelled, o.user_context "
2434 "FROM hosts AS h "
2435 "LEFT JOIN dhcp4_options AS o "
2436 "ON h.host_id = o.host_id "
2437 "WHERE h.dhcp4_subnet_id = ? AND h.ipv4_address = ? "
2438 "ORDER BY h.host_id, o.option_id"},
2439
2440 // Retrieves host information, IPv6 reservations and DHCPv6 options
2441 // associated with a host using prefix and prefix length. This query
2442 // returns host information for a single host. However, multiple rows
2443 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2444 // The number of rows returned is multiplication of number of existing
2445 // IPv6 reservations and DHCPv6 options.
2447 "SELECT h.host_id, h.dhcp_identifier, "
2448 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2449 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2450 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2451 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2452 "h.dhcp4_boot_file_name, h.auth_key, "
2453 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2454 "o.persistent, o.cancelled, o.user_context,"
2455 "r.reservation_id, r.address, r.prefix_len, r.type, "
2456 "r.dhcp6_iaid "
2457 "FROM hosts AS h "
2458 "LEFT JOIN dhcp6_options AS o "
2459 "ON h.host_id = o.host_id "
2460 "LEFT JOIN ipv6_reservations AS r "
2461 "ON h.host_id = r.host_id "
2462 "WHERE h.host_id = "
2463 "( SELECT host_id FROM ipv6_reservations "
2464 "WHERE address = ? AND prefix_len = ? ) "
2465 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2466
2467 // Retrieves host information, IPv6 reservations and DHCPv6 options
2468 // associated with a host using IPv6 subnet id and prefix. This query
2469 // returns host information for a single host. However, multiple rows
2470 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2471 // The number of rows returned is multiplication of number of existing
2472 // IPv6 reservations and DHCPv6 options.
2474 "SELECT h.host_id, h.dhcp_identifier, "
2475 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2476 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2477 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2478 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2479 "h.dhcp4_boot_file_name, h.auth_key, "
2480 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2481 "o.persistent, o.cancelled, o.user_context, "
2482 "r.reservation_id, r.address, r.prefix_len, r.type, "
2483 "r.dhcp6_iaid "
2484 "FROM hosts AS h "
2485 "LEFT JOIN dhcp6_options AS o "
2486 "ON h.host_id = o.host_id "
2487 "LEFT JOIN ipv6_reservations AS r "
2488 "ON h.host_id = r.host_id "
2489 "WHERE h.dhcp6_subnet_id = ? AND h.host_id IN "
2490 "(SELECT host_id FROM ipv6_reservations "
2491 "WHERE address = ?) "
2492 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2493
2494 // Retrieves host information, IPv6 reservations and DHCPv6 options
2495 // associated with a host using IPv6 address/prefix. This query
2496 // may return host information for one or more host reservations. Even
2497 // if only one host is found, multiple rows
2498 // are returned due to left joining IPv6 reservations and DHCPv6 options.
2499 // The number of rows returned is multiplication of number of existing
2500 // IPv6 reservations and DHCPv6 options.
2502 "SELECT h.host_id, h.dhcp_identifier, "
2503 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2504 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2505 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2506 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2507 "h.dhcp4_boot_file_name, h.auth_key, "
2508 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2509 "o.persistent, o.cancelled, o.user_context, "
2510 "r.reservation_id, r.address, r.prefix_len, r.type, "
2511 "r.dhcp6_iaid "
2512 "FROM hosts AS h "
2513 "LEFT JOIN dhcp6_options AS o "
2514 "ON h.host_id = o.host_id "
2515 "LEFT JOIN ipv6_reservations AS r "
2516 "ON h.host_id = r.host_id "
2517 "WHERE h.host_id IN "
2518 "(SELECT host_id FROM ipv6_reservations "
2519 "WHERE address = ?) "
2520 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2521
2522 // Retrieves host information along with the DHCPv4 options associated with
2523 // it. Left joining the dhcp4_options table results in multiple rows being
2524 // returned for the same host. Hosts are retrieved by IPv4 subnet id.
2526 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2527 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2528 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2529 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2530 "h.dhcp4_boot_file_name, h.auth_key, "
2531 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2532 "o.persistent, o.cancelled, o.user_context "
2533 "FROM hosts AS h "
2534 "LEFT JOIN dhcp4_options AS o "
2535 "ON h.host_id = o.host_id "
2536 "WHERE h.dhcp4_subnet_id = ? "
2537 "ORDER BY h.host_id, o.option_id"},
2538
2539 // Retrieves host information, IPv6 reservations and DHCPv6 options
2540 // associated with a host. The number of rows returned is a multiplication
2541 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2542 // by IPv6 subnet id.
2544 "SELECT h.host_id, h.dhcp_identifier, "
2545 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2546 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2547 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2548 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2549 "h.dhcp4_boot_file_name, h.auth_key, "
2550 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2551 "o.persistent, o.cancelled, o.user_context, "
2552 "r.reservation_id, r.address, r.prefix_len, r.type, "
2553 "r.dhcp6_iaid "
2554 "FROM hosts AS h "
2555 "LEFT JOIN dhcp6_options AS o "
2556 "ON h.host_id = o.host_id "
2557 "LEFT JOIN ipv6_reservations AS r "
2558 "ON h.host_id = r.host_id "
2559 "WHERE h.dhcp6_subnet_id = ? "
2560 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2561
2562 // Retrieves host information, IPv6 reservations and both DHCPv4 and
2563 // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2564 // to retrieve information from 4 different tables using a single query.
2565 // Hence, this query returns multiple rows for a single host.
2567 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2568 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2569 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2570 "h.user_context, "
2571 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2572 "h.dhcp4_boot_file_name, h.auth_key, "
2573 "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2574 "o4.persistent, o4.cancelled, o4.user_context, "
2575 "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2576 "o6.persistent, o6.cancelled, o6.user_context, "
2577 "r.reservation_id, r.address, r.prefix_len, r.type, "
2578 "r.dhcp6_iaid "
2579 "FROM hosts AS h "
2580 "LEFT JOIN dhcp4_options AS o4 "
2581 "ON h.host_id = o4.host_id "
2582 "LEFT JOIN dhcp6_options AS o6 "
2583 "ON h.host_id = o6.host_id "
2584 "LEFT JOIN ipv6_reservations AS r "
2585 "ON h.host_id = r.host_id "
2586 "WHERE h.hostname = ? "
2587 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2588
2589 // Retrieves host information and DHCPv4 options using hostname and
2590 // subnet identifier. Left joining the dhcp4_options table results in
2591 // multiple rows being returned for the same host.
2593 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2594 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2595 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2596 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2597 "h.dhcp4_boot_file_name, h.auth_key, "
2598 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2599 "o.persistent, o.cancelled, o.user_context "
2600 "FROM hosts AS h "
2601 "LEFT JOIN dhcp4_options AS o "
2602 "ON h.host_id = o.host_id "
2603 "WHERE h.hostname = ? AND h.dhcp4_subnet_id = ? "
2604 "ORDER BY h.host_id, o.option_id"},
2605
2606 // Retrieves host information, IPv6 reservations and DHCPv6 options
2607 // using hostname and subnet identifier. The number of rows returned
2608 // is a multiplication of number of IPv6 reservations and DHCPv6 options.
2610 "SELECT h.host_id, h.dhcp_identifier, "
2611 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2612 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2613 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2614 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2615 "h.dhcp4_boot_file_name, h.auth_key, "
2616 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2617 "o.persistent, o.cancelled, o.user_context, "
2618 "r.reservation_id, r.address, r.prefix_len, r.type, "
2619 "r.dhcp6_iaid "
2620 "FROM hosts AS h "
2621 "LEFT JOIN dhcp6_options AS o "
2622 "ON h.host_id = o.host_id "
2623 "LEFT JOIN ipv6_reservations AS r "
2624 "ON h.host_id = r.host_id "
2625 "WHERE h.hostname = ? AND h.dhcp6_subnet_id = ? "
2626 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2627
2628 // Retrieves host information along with the DHCPv4 options associated with
2629 // it. Left joining the dhcp4_options table results in multiple rows being
2630 // returned for the same host. Hosts are retrieved by IPv4 subnet id
2631 // and with a host id greater than the start one.
2632 // The number of hosts returned is lower or equal to the limit.
2634 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2635 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2636 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2637 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2638 "h.dhcp4_boot_file_name, h.auth_key, "
2639 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2640 "o.persistent, o.cancelled, o.user_context "
2641 "FROM ( SELECT * FROM hosts AS h "
2642 "WHERE h.dhcp4_subnet_id = ? AND h.host_id > ? "
2643 "ORDER BY h.host_id "
2644 "LIMIT ? ) AS h "
2645 "LEFT JOIN dhcp4_options AS o "
2646 "ON h.host_id = o.host_id "
2647 "ORDER BY h.host_id, o.option_id"},
2648
2649 // Retrieves host information, IPv6 reservations and DHCPv6 options
2650 // associated with a host. The number of rows returned is a multiplication
2651 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2652 // by IPv6 subnet id and with a host id greater than the start one.
2653 // The number of hosts returned is lower or equal to the limit.
2655 "SELECT h.host_id, h.dhcp_identifier, "
2656 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2657 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2658 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2659 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2660 "h.dhcp4_boot_file_name, h.auth_key, "
2661 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2662 "o.persistent, o.cancelled, o.user_context, "
2663 "r.reservation_id, r.address, r.prefix_len, r.type, "
2664 "r.dhcp6_iaid "
2665 "FROM ( SELECT * FROM hosts AS h "
2666 "WHERE h.dhcp6_subnet_id = ? AND h.host_id > ? "
2667 "ORDER BY h.host_id "
2668 "LIMIT ? ) AS h "
2669 "LEFT JOIN dhcp6_options AS o "
2670 "ON h.host_id = o.host_id "
2671 "LEFT JOIN ipv6_reservations AS r "
2672 "ON h.host_id = r.host_id "
2673 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2674
2675 // Retrieves host information along with the DHCPv4 options associated with
2676 // it. Left joining the dhcp4_options table results in multiple rows being
2677 // returned for the same host. Hosts are retrieved with a host id greater
2678 // than the start one.
2679 // The number of hosts returned is lower or equal to the limit.
2681 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2682 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2683 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2684 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2685 "h.dhcp4_boot_file_name, h.auth_key, "
2686 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2687 "o.persistent, o.cancelled, o.user_context "
2688 "FROM ( SELECT * FROM hosts AS h "
2689 "WHERE h.host_id > ? "
2690 "ORDER BY h.host_id "
2691 "LIMIT ? ) AS h "
2692 "LEFT JOIN dhcp4_options AS o "
2693 "ON h.host_id = o.host_id "
2694 "ORDER BY h.host_id, o.option_id"},
2695
2696 // Retrieves host information, IPv6 reservations and DHCPv6 options
2697 // associated with a host. The number of rows returned is a multiplication
2698 // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2699 // with a host id greater than the start one.
2700 // The number of hosts returned is lower or equal to the limit.
2702 "SELECT h.host_id, h.dhcp_identifier, "
2703 "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2704 "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2705 "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2706 "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2707 "h.dhcp4_boot_file_name, h.auth_key, "
2708 "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2709 "o.persistent, o.cancelled, o.user_context, "
2710 "r.reservation_id, r.address, r.prefix_len, r.type, "
2711 "r.dhcp6_iaid "
2712 "FROM ( SELECT * FROM hosts AS h "
2713 "WHERE h.host_id > ? "
2714 "ORDER BY h.host_id "
2715 "LIMIT ? ) AS h "
2716 "LEFT JOIN dhcp6_options AS o "
2717 "ON h.host_id = o.host_id "
2718 "LEFT JOIN ipv6_reservations AS r "
2719 "ON h.host_id = r.host_id "
2720 "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2721
2722 // Inserts a host into the 'hosts' table without checking that there is
2723 // a reservation for the IP address.
2725 "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2726 "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2727 "dhcp4_client_classes, dhcp6_client_classes, "
2728 "user_context, dhcp4_next_server, "
2729 "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2730 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
2731
2732 // Inserts a host into the 'hosts' table with checking that reserved IP
2733 // address is unique. The innermost query checks if there is at least
2734 // one host for the given IP/subnet combination. For checking whether
2735 // hosts exists or not it doesn't matter if we select actual columns,
2736 // thus SELECT 1 was used as an optimization to avoid selecting data
2737 // that will be ignored anyway. If the host does not exist the new
2738 // host is inserted. DUAL is a special MySQL table from which we can
2739 // select the values to be inserted. If the host with the given IP
2740 // address already exists the new host won't be inserted. The caller
2741 // can check the number of affected rows to detect that there was
2742 // a duplicate host in the database.
2744 "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2745 "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2746 "dhcp4_client_classes, dhcp6_client_classes, "
2747 "user_context, dhcp4_next_server, "
2748 "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2749 "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? FROM DUAL "
2750 "WHERE NOT EXISTS ("
2751 "SELECT 1 FROM hosts "
2752 "WHERE ipv4_address = ? AND dhcp4_subnet_id = ? "
2753 "LIMIT 1"
2754 ")"},
2755
2756 // Inserts a single IPv6 reservation into 'reservations' table without
2757 // checking that the inserted reservation is unique.
2759 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2760 "dhcp6_iaid, host_id) "
2761 "VALUES (?, ?, ?, ?, ?)"},
2762
2763 // Inserts a single IPv6 reservation into 'reservations' table with
2764 // checking that the inserted reservation is unique.
2766 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2767 "dhcp6_iaid, host_id) "
2768 "SELECT ?, ?, ?, ?, ? FROM DUAL "
2769 "WHERE NOT EXISTS ("
2770 "SELECT 1 FROM ipv6_reservations "
2771 "WHERE address = ? AND prefix_len = ? "
2772 "LIMIT 1"
2773 ")"},
2774
2775 // Inserts a single DHCPv4 option into 'dhcp4_options' table.
2776 // Using fixed scope_id = 3, which associates an option with host.
2778 "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
2779 "persistent, cancelled, user_context, dhcp4_subnet_id, host_id, scope_id) "
2780 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2781
2782 // Inserts a single DHCPv6 option into 'dhcp6_options' table.
2783 // Using fixed scope_id = 3, which associates an option with host.
2785 "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
2786 "persistent, cancelled, user_context, dhcp6_subnet_id, host_id, scope_id) "
2787 "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2788
2789 // Delete IPv4 reservations by subnet id and reserved address.
2791 "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND ipv4_address = ?"},
2792
2793 // Delete IPv6 reservations by subnet id and reserved address/prefix.
2795 "DELETE h FROM hosts AS h "
2796 "INNER JOIN ipv6_reservations AS r "
2797 "ON h.host_id = r.host_id "
2798 "WHERE h.dhcp6_subnet_id = ? AND r.address = ?"},
2799
2800 // Delete a single IPv4 reservation by subnet id and identifier.
2802 "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type=? "
2803 "AND dhcp_identifier = ?"},
2804
2805 // Delete a single IPv6 reservation by subnet id and identifier.
2807 "DELETE FROM hosts WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type=? "
2808 "AND dhcp_identifier = ?"}
2809 }
2810};
2811
2812} // namespace
2813
2814// MySqlHostContext Constructor
2815
2817 IOServiceAccessorPtr io_service_accessor,
2818 db::DbCallback db_reconnect_callback)
2819 : conn_(parameters, io_service_accessor, db_reconnect_callback),
2820 is_readonly_(true) {
2821}
2822
2823// MySqlHostContextAlloc Constructor and Destructor
2824
2826 MySqlHostDataSourceImpl& mgr) : ctx_(), mgr_(mgr) {
2827
2828 if (MultiThreadingMgr::instance().getMode()) {
2829 // multi-threaded
2830 {
2831 // we need to protect the whole pool_ operation, hence extra scope {}
2832 lock_guard<mutex> lock(mgr_.pool_->mutex_);
2833 if (!mgr_.pool_->pool_.empty()) {
2834 ctx_ = mgr_.pool_->pool_.back();
2835 mgr_.pool_->pool_.pop_back();
2836 }
2837 }
2838 if (!ctx_) {
2839 ctx_ = mgr_.createContext();
2840 }
2841 } else {
2842 // single-threaded
2843 if (mgr_.pool_->pool_.empty()) {
2844 isc_throw(Unexpected, "No available MySQL host context?!");
2845 }
2846 ctx_ = mgr_.pool_->pool_.back();
2847 }
2848}
2849
2851 if (MultiThreadingMgr::instance().getMode()) {
2852 // multi-threaded
2853 lock_guard<mutex> lock(mgr_.pool_->mutex_);
2854 mgr_.pool_->pool_.push_back(ctx_);
2855 if (ctx_->conn_.isUnusable()) {
2856 mgr_.unusable_ = true;
2857 }
2858 } else if (ctx_->conn_.isUnusable()) {
2859 mgr_.unusable_ = true;
2860 }
2861}
2862
2864 : parameters_(parameters), ip_reservations_unique_(true), unusable_(false) {
2865
2866 // Create unique timer name per instance.
2867 timer_name_ = "MySqlHostMgr[";
2868 timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
2869 timer_name_ += "]DbReconnectTimer";
2870
2873 timer_name_);
2874
2875 // Create an initial context.
2876 pool_.reset(new MySqlHostContextPool());
2877 pool_->pool_.push_back(createContext());
2878}
2879
2880// Create context.
2881
2887
2888 // Open the database.
2889 ctx->conn_.openDatabase();
2890
2891 // Check if we have TLS when we required it.
2892 if (ctx->conn_.getTls()) {
2893 std::string cipher = ctx->conn_.getTlsCipher();
2894 if (cipher.empty()) {
2896 } else {
2899 .arg(cipher);
2900 }
2901 }
2902
2903 // Prepare query statements. Those are will be only used to retrieve
2904 // information from the database, so they can be used even if the
2905 // database is read only for the current user.
2906 ctx->conn_.prepareStatements(tagged_statements.begin(),
2907 tagged_statements.begin() + WRITE_STMTS_BEGIN);
2908
2909 // Check if the backend is explicitly configured to operate with
2910 // read only access to the database.
2911 ctx->is_readonly_ = ctx->conn_.configuredReadOnly();
2912
2913 // If we are using read-write mode for the database we also prepare
2914 // statements for INSERTS etc.
2915 if (!ctx->is_readonly_) {
2916 // Prepare statements for writing to the database, e.g. INSERT.
2917 ctx->conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
2918 tagged_statements.end());
2919 } else {
2921 }
2922
2923 // Create the exchange objects for use in exchanging data between the
2924 // program and the database.
2925 ctx->host_ipv4_exchange_.reset(new MySqlHostWithOptionsExchange(MySqlHostWithOptionsExchange::DHCP4_ONLY));
2926 ctx->host_ipv6_exchange_.reset(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP6_ONLY));
2927 ctx->host_ipv46_exchange_.reset(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP4_AND_DHCP6));
2928 ctx->host_ipv6_reservation_exchange_.reset(new MySqlIPv6ReservationExchange());
2929 ctx->host_option_exchange_.reset(new MySqlOptionExchange());
2930
2931 // Create ReconnectCtl for this connection.
2932 ctx->conn_.makeReconnectCtl(timer_name_);
2933
2934 return (ctx);
2935}
2936
2939
2940bool
2943
2944 // Invoke application layer connection lost callback.
2945 if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
2946 return (false);
2947 }
2948
2949 bool reopened = false;
2950
2951 const std::string timer_name = db_reconnect_ctl->timerName();
2952
2953 // At least one connection was lost.
2954 try {
2955 CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
2956 std::list<std::string> host_db_access_list = cfg_db->getHostDbAccessStringList();
2957 for (std::string& hds : host_db_access_list) {
2958 auto parameters = DatabaseConnection::parse(hds);
2959 if (HostMgr::delBackend("mysql", hds, true)) {
2961 }
2962 }
2963 reopened = true;
2964 } catch (const std::exception& ex) {
2966 .arg(ex.what());
2967 }
2968
2969 if (reopened) {
2970 // Cancel the timer.
2971 if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2972 TimerMgr::instance()->unregisterTimer(timer_name);
2973 }
2974
2975 // Invoke application layer connection recovered callback.
2976 if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
2977 return (false);
2978 }
2979 } else {
2980 if (!db_reconnect_ctl->checkRetries()) {
2981 // We're out of retries, log it and initiate shutdown.
2983 .arg(db_reconnect_ctl->maxRetries());
2984
2985 // Cancel the timer.
2986 if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2987 TimerMgr::instance()->unregisterTimer(timer_name);
2988 }
2989
2990 // Invoke application layer connection failed callback.
2992 return (false);
2993 }
2994
2996 .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
2997 .arg(db_reconnect_ctl->maxRetries())
2998 .arg(db_reconnect_ctl->retryInterval());
2999
3000 // Start the timer.
3001 if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
3002 TimerMgr::instance()->registerTimer(timer_name,
3003 std::bind(&MySqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl),
3004 db_reconnect_ctl->retryInterval(),
3006 }
3007 TimerMgr::instance()->setup(timer_name);
3008 }
3009
3010 return (true);
3011}
3012
3013std::pair<uint32_t, uint32_t>
3022
3023void
3025 StatementIndex stindex,
3026 std::vector<MYSQL_BIND>& bind) {
3027 // Bind the parameters to the statement
3028 int status = mysql_stmt_bind_param(ctx->conn_.getStatement(stindex), &bind[0]);
3029 checkError(ctx, status, stindex, "unable to bind parameters");
3030
3031 // Execute the statement
3032 status = MysqlExecuteStatement(ctx->conn_.getStatement(stindex));
3033
3034 if (status != 0) {
3035 // Failure: check for the special case of duplicate entry.
3036 if (mysql_errno(ctx->conn_.mysql_) == ER_DUP_ENTRY) {
3037 isc_throw(DuplicateEntry, "Database duplicate entry error");
3038 }
3039 checkError(ctx, status, stindex, "unable to execute");
3040 }
3041
3042 // If the number of rows inserted is 0 it means that the query detected
3043 // an attempt to insert duplicated data for which there is no unique
3044 // index in the database. Unique indexes are not created in the database
3045 // when it may be sometimes allowed to insert duplicated records per
3046 // server's configuration.
3047 my_ulonglong numrows = mysql_stmt_affected_rows(ctx->conn_.getStatement(stindex));
3048 if (numrows == 0) {
3049 isc_throw(DuplicateEntry, "Database duplicate entry error");
3050 }
3051}
3052
3053bool
3055 StatementIndex stindex,
3056 MYSQL_BIND* bind) {
3057 // Bind the parameters to the statement
3058 int status = mysql_stmt_bind_param(ctx->conn_.getStatement(stindex), &bind[0]);
3059 checkError(ctx, status, stindex, "unable to bind parameters");
3060
3061 // Execute the statement
3062 status = MysqlExecuteStatement(ctx->conn_.getStatement(stindex));
3063
3064 if (status != 0) {
3065 checkError(ctx, status, stindex, "unable to execute");
3066 }
3067
3068 // Let's check how many hosts were deleted.
3069 my_ulonglong numrows = mysql_stmt_affected_rows(ctx->conn_.getStatement(stindex));
3070
3071 return (numrows != 0);
3072}
3073
3074void
3076 const IPv6Resrv& resv,
3077 const HostID& id) {
3078 std::vector<MYSQL_BIND> bind = ctx->host_ipv6_reservation_exchange_->
3079 createBindForSend(resv, id, ip_reservations_unique_);
3080
3082}
3083
3084void
3086 const StatementIndex& stindex,
3087 const OptionDescriptor& opt_desc,
3088 const std::string& opt_space,
3089 const Optional<SubnetID>& subnet_id,
3090 const HostID& id) {
3091 std::vector<MYSQL_BIND> bind = ctx->host_option_exchange_->createBindForSend(opt_desc, opt_space, subnet_id, id);
3092
3093 addStatement(ctx, stindex, bind);
3094}
3095
3096void
3098 const StatementIndex& stindex,
3099 const ConstCfgOptionPtr& options_cfg,
3100 const uint64_t host_id) {
3101 // Get option space names and vendor space names and combine them within a
3102 // single list.
3103 std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
3104 std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
3105 option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
3106 vendor_spaces.end());
3107
3108 // For each option space retrieve all options and insert them into the
3109 // database.
3110 for (auto const& space : option_spaces) {
3111 OptionContainerPtr options = options_cfg->getAllCombined(space);
3112 if (options && !options->empty()) {
3113 for (auto const& opt : *options) {
3114 addOption(ctx, stindex, opt, space, Optional<SubnetID>(), host_id);
3115 }
3116 }
3117 }
3118}
3119
3120void
3122 const int status,
3123 const StatementIndex index,
3124 const char* what) const {
3125 ctx->conn_.checkError(status, index, what);
3126}
3127
3128void
3130 StatementIndex stindex,
3131 MYSQL_BIND* bind,
3132 boost::shared_ptr<MySqlHostExchange> exchange,
3133 ConstHostCollection& result,
3134 bool single) const {
3135
3136 // Bind the selection parameters to the statement
3137 int status = mysql_stmt_bind_param(ctx->conn_.getStatement(stindex), bind);
3138 checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
3139
3140 // Set up the MYSQL_BIND array for the data being returned and bind it to
3141 // the statement.
3142 std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
3143 status = mysql_stmt_bind_result(ctx->conn_.getStatement(stindex), &outbind[0]);
3144 checkError(ctx, status, stindex, "unable to bind SELECT clause parameters");
3145
3146 // Execute the statement
3147 status = MysqlExecuteStatement(ctx->conn_.getStatement(stindex));
3148 checkError(ctx, status, stindex, "unable to execute");
3149
3150 // Ensure that all the lease information is retrieved in one go to avoid
3151 // overhead of going back and forth between client and server.
3152 status = mysql_stmt_store_result(ctx->conn_.getStatement(stindex));
3153 checkError(ctx, status, stindex, "unable to set up for storing all results");
3154
3155 // Set up the fetch "release" object to release resources associated
3156 // with the call to mysql_stmt_fetch when this method exits, then
3157 // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
3158 // successful data fetch.
3159 MySqlFreeResult fetch_release(ctx->conn_.getStatement(stindex));
3160 while ((status = mysql_stmt_fetch(ctx->conn_.getStatement(stindex))) ==
3162 try {
3163 exchange->processFetchedData(result);
3164
3165 } catch (const isc::BadValue& ex) {
3166 // Rethrow the exception with a bit more data.
3167 isc_throw(BadValue, ex.what() << ". Statement is <" <<
3168 ctx->conn_.text_statements_[stindex] << ">");
3169 }
3170
3171 if (single && (result.size() > 1)) {
3172 isc_throw(MultipleRecords, "multiple records were found in the "
3173 "database where only one was expected for query "
3174 << ctx->conn_.text_statements_[stindex]);
3175 }
3176 }
3177
3178 // How did the fetch end?
3179 // If mysql_stmt_fetch return value is equal to 1 an error occurred.
3180 if (status == MLM_MYSQL_FETCH_FAILURE) {
3181 // Error - unable to fetch results
3182 checkError(ctx, status, stindex, "unable to fetch results");
3183
3184 } else if (status == MYSQL_DATA_TRUNCATED) {
3185 // Data truncated - throw an exception indicating what was at fault
3186 isc_throw(DataTruncated, ctx->conn_.text_statements_[stindex]
3187 << " returned truncated data: columns affected are "
3188 << exchange->getErrorColumns());
3189 }
3190}
3191
3194 const SubnetID& subnet_id,
3195 const Host::IdentifierType& identifier_type,
3196 const uint8_t* identifier_begin,
3197 const size_t identifier_len,
3198 StatementIndex stindex,
3199 boost::shared_ptr<MySqlHostExchange> exchange) const {
3200
3201 // Set up the WHERE clause value
3202 MYSQL_BIND inbind[3];
3203 memset(inbind, 0, sizeof(inbind));
3204
3205 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3206 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3207 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3208 inbind[0].is_unsigned = MLM_TRUE;
3209
3210 // Identifier value.
3211 std::vector<char> identifier_vec(identifier_begin,
3212 identifier_begin + identifier_len);
3213 unsigned long length = identifier_vec.size();
3214 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3215 inbind[2].buffer = &identifier_vec[0];
3216 inbind[2].buffer_length = length;
3217 inbind[2].length = &length;
3218
3219 // Identifier type.
3220 char identifier_type_copy = static_cast<char>(identifier_type);
3221 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3222 inbind[1].buffer = &identifier_type_copy;
3223 inbind[1].is_unsigned = MLM_TRUE;
3224
3225 ConstHostCollection collection;
3226 getHostCollection(ctx, stindex, inbind, exchange, collection, true);
3227
3228 // Return single record if present, else clear the host.
3229 ConstHostPtr result;
3230 if (!collection.empty()) {
3231 result = *collection.begin();
3232 }
3233
3234 return (result);
3235}
3236
3237void
3239 if (ctx->is_readonly_) {
3240 isc_throw(ReadOnlyDb, "MySQL host database backend is configured to"
3241 " operate in read only mode");
3242 }
3243}
3244
3248
3251
3254 return (impl_->parameters_);
3255}
3256
3257void
3259 // Get a context
3260 MySqlHostContextAlloc get_context(*impl_);
3261 MySqlHostContextPtr ctx = get_context.ctx_;
3262
3263 // If operating in read-only mode, throw exception.
3264 impl_->checkReadOnly(ctx);
3265
3266 // Initiate MySQL transaction as we will have to make multiple queries
3267 // to insert host information into multiple tables. If that fails on
3268 // any stage, the transaction will be rolled back by the destructor of
3269 // the MySqlTransaction class.
3270 MySqlTransaction transaction(ctx->conn_);
3271
3272 // If we're configured to check that an IP reservation within a given subnet
3273 // is unique, the IP reservation exists and the subnet is actually set
3274 // we will be using a special query that checks for uniqueness. Otherwise,
3275 // we will use a regular insert statement.
3276 bool unique_ip = impl_->ip_reservations_unique_ && !host->getIPv4Reservation().isV4Zero()
3277 && host->getIPv4SubnetID() != SUBNET_ID_UNUSED;
3278
3279 // Create the MYSQL_BIND array for the host
3280 std::vector<MYSQL_BIND> bind = ctx->host_ipv4_exchange_->createBindForSend(host, unique_ip);
3281
3282 // ... and insert the host.
3283 impl_->addStatement(ctx, unique_ip ? MySqlHostDataSourceImpl::INSERT_HOST_UNIQUE_IP :
3285
3286 // Gets the last inserted hosts id
3287 uint64_t host_id = mysql_insert_id(ctx->conn_.mysql_);
3288
3289 // Insert DHCPv4 options.
3290 ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
3291 if (cfg_option4) {
3293 cfg_option4, host_id);
3294 }
3295
3296 // Insert DHCPv6 options.
3297 ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
3298 if (cfg_option6) {
3300 cfg_option6, host_id);
3301 }
3302
3303 // Insert IPv6 reservations.
3304 IPv6ResrvRange v6resv = host->getIPv6Reservations();
3305 if (std::distance(v6resv.first, v6resv.second) > 0) {
3306 BOOST_FOREACH(auto const& resv, v6resv) {
3307 impl_->addResv(ctx, resv.second, host_id);
3308 }
3309 }
3310
3311 // Everything went fine, so explicitly commit the transaction.
3312 transaction.commit();
3313}
3314
3315bool
3317 const asiolink::IOAddress& addr) {
3318 // Get a context
3319 MySqlHostContextAlloc get_context(*impl_);
3320 MySqlHostContextPtr ctx = get_context.ctx_;
3321
3322 // If operating in read-only mode, throw exception.
3323 impl_->checkReadOnly(ctx);
3324
3325 // Set up the WHERE clause value
3326 MYSQL_BIND inbind[2];
3327
3328 uint32_t subnet = subnet_id;
3329 memset(inbind, 0, sizeof(inbind));
3330 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3331 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3332 inbind[0].is_unsigned = MLM_TRUE;
3333
3334 // v4
3335 if (addr.isV4()) {
3336 uint32_t addr4 = addr.toUint32();
3337 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3338 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3339 inbind[1].is_unsigned = MLM_TRUE;
3340
3341 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_ADDR4, inbind));
3342 }
3343
3344 // v6
3345 std::vector<uint8_t>addr6 = addr.toBytes();
3346 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
3347 isc_throw(DbOperationError, "del() - address is not "
3348 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
3349 }
3350
3351 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
3352 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3353 inbind[1].buffer = reinterpret_cast<char*>(&addr6[0]);
3354 inbind[1].buffer_length = isc::asiolink::V6ADDRESS_LEN;
3355 inbind[1].length = &addr6_length;
3356
3357 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_ADDR6, inbind));
3358}
3359
3360bool
3362 const Host::IdentifierType& identifier_type,
3363 const uint8_t* identifier_begin,
3364 const size_t identifier_len) {
3365 // Get a context
3366 MySqlHostContextAlloc get_context(*impl_);
3367 MySqlHostContextPtr ctx = get_context.ctx_;
3368
3369 // If operating in read-only mode, throw exception.
3370 impl_->checkReadOnly(ctx);
3371
3372 // Set up the WHERE clause value
3373 MYSQL_BIND inbind[3];
3374
3375 // subnet-id
3376 memset(inbind, 0, sizeof(inbind));
3377 uint32_t subnet = static_cast<uint32_t>(subnet_id);
3378 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3379 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3380 inbind[0].is_unsigned = MLM_TRUE;
3381
3382 // identifier type
3383 char identifier_type_copy = static_cast<char>(identifier_type);
3384 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3385 inbind[1].buffer = &identifier_type_copy;
3386 inbind[1].is_unsigned = MLM_TRUE;
3387
3388 // identifier value
3389 std::vector<char> identifier_vec(identifier_begin,
3390 identifier_begin + identifier_len);
3391 unsigned long length = identifier_vec.size();
3392 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3393 inbind[2].buffer = &identifier_vec[0];
3394 inbind[2].buffer_length = length;
3395 inbind[2].length = &length;
3396
3397 ConstHostCollection collection;
3398 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_SUBID4_ID, inbind));
3399}
3400
3401bool
3403 const Host::IdentifierType& identifier_type,
3404 const uint8_t* identifier_begin,
3405 const size_t identifier_len) {
3406 // Get a context
3407 MySqlHostContextAlloc get_context(*impl_);
3408 MySqlHostContextPtr ctx = get_context.ctx_;
3409
3410 // If operating in read-only mode, throw exception.
3411 impl_->checkReadOnly(ctx);
3412
3413 // Set up the WHERE clause value
3414 MYSQL_BIND inbind[3];
3415
3416 // subnet-id
3417 memset(inbind, 0, sizeof(inbind));
3418 uint32_t subnet = static_cast<uint32_t>(subnet_id);
3419 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3420 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3421 inbind[0].is_unsigned = MLM_TRUE;
3422
3423 // identifier type
3424 char identifier_type_copy = static_cast<char>(identifier_type);
3425 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3426 inbind[1].buffer = &identifier_type_copy;
3427 inbind[1].is_unsigned = MLM_TRUE;
3428
3429 // identifier value
3430 std::vector<char> identifier_vec(identifier_begin,
3431 identifier_begin + identifier_len);
3432 unsigned long length = identifier_vec.size();
3433 inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3434 inbind[2].buffer = &identifier_vec[0];
3435 inbind[2].buffer_length = length;
3436 inbind[2].length = &length;
3437
3438 ConstHostCollection collection;
3439 return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_SUBID6_ID, inbind));
3440}
3441
3444 const uint8_t* identifier_begin,
3445 const size_t identifier_len) const {
3446 // Get a context
3447 MySqlHostContextAlloc get_context(*impl_);
3448 MySqlHostContextPtr ctx = get_context.ctx_;
3449
3450 // Set up the WHERE clause value
3451 MYSQL_BIND inbind[2];
3452 memset(inbind, 0, sizeof(inbind));
3453
3454 // Identifier type.
3455 char identifier_type_copy = static_cast<char>(identifier_type);
3456 inbind[1].buffer = &identifier_type_copy;
3457 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3458 inbind[1].is_unsigned = MLM_TRUE;
3459
3460 // Identifier value.
3461 std::vector<char> identifier_vec(identifier_begin,
3462 identifier_begin + identifier_len);
3463 unsigned long int length = identifier_vec.size();
3464 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
3465 inbind[0].buffer = &identifier_vec[0];
3466 inbind[0].buffer_length = length;
3467 inbind[0].length = &length;
3468
3469 ConstHostCollection result;
3470 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_DHCPID, inbind,
3471 ctx->host_ipv46_exchange_, result, false);
3472
3473 return (result);
3474}
3475
3478 // Get a context
3479 MySqlHostContextAlloc get_context(*impl_);
3480 MySqlHostContextPtr ctx = get_context.ctx_;
3481
3482 // Set up the WHERE clause value
3483 MYSQL_BIND inbind[1];
3484 memset(inbind, 0, sizeof(inbind));
3485 uint32_t subnet = subnet_id;
3486 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3487 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3488 inbind[0].is_unsigned = MLM_TRUE;
3489
3490 ConstHostCollection result;
3491 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID4, inbind,
3492 ctx->host_ipv4_exchange_, result, false);
3493
3494 return (result);
3495}
3496
3499 // Get a context
3500 MySqlHostContextAlloc get_context(*impl_);
3501 MySqlHostContextPtr ctx = get_context.ctx_;
3502
3503 // Set up the WHERE clause value
3504 MYSQL_BIND inbind[1];
3505 memset(inbind, 0, sizeof(inbind));
3506 uint32_t subnet = subnet_id;
3507 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3508 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3509 inbind[0].is_unsigned = MLM_TRUE;
3510
3511 ConstHostCollection result;
3512 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6, inbind,
3513 ctx->host_ipv6_exchange_, result, false);
3514
3515 return (result);
3516}
3517
3519MySqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
3520 // Get a context
3521 MySqlHostContextAlloc get_context(*impl_);
3522 MySqlHostContextPtr ctx = get_context.ctx_;
3523
3524 // Set up the WHERE clause value
3525 MYSQL_BIND inbind[1];
3526 memset(inbind, 0, sizeof(inbind));
3527
3528 // Hostname
3529 char hostname_[HOSTNAME_MAX_LEN];
3530 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3531 unsigned long length = hostname.length();
3532 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3533 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3534 inbind[0].buffer_length = length;
3535 inbind[0].length = &length;
3536
3537 ConstHostCollection result;
3538 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME, inbind,
3539 ctx->host_ipv46_exchange_, result, false);
3540
3541 return (result);
3542}
3543
3545MySqlHostDataSource::getAllbyHostname4(const std::string& hostname,
3546 const SubnetID& subnet_id) const {
3547 // Get a context
3548 MySqlHostContextAlloc get_context(*impl_);
3549 MySqlHostContextPtr ctx = get_context.ctx_;
3550
3551 // Set up the WHERE clause value
3552 MYSQL_BIND inbind[2];
3553 memset(inbind, 0, sizeof(inbind));
3554
3555 // Hostname
3556 char hostname_[HOSTNAME_MAX_LEN];
3557 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3558 unsigned long length = hostname.length();
3559 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3560 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3561 inbind[0].buffer_length = length;
3562 inbind[0].length = &length;
3563
3564 // Subnet ID
3565 uint32_t subnet = subnet_id;
3566 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3567 inbind[1].buffer = reinterpret_cast<char*>(&subnet);
3568 inbind[1].is_unsigned = MLM_TRUE;
3569
3570 ConstHostCollection result;
3571 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4, inbind,
3572 ctx->host_ipv4_exchange_, result, false);
3573
3574 return (result);
3575}
3576
3578MySqlHostDataSource::getAllbyHostname6(const std::string& hostname,
3579 const SubnetID& subnet_id) const {
3580 // Get a context
3581 MySqlHostContextAlloc get_context(*impl_);
3582 MySqlHostContextPtr ctx = get_context.ctx_;
3583
3584 // Set up the WHERE clause value
3585 MYSQL_BIND inbind[2];
3586 memset(inbind, 0, sizeof(inbind));
3587
3588 // Hostname
3589 char hostname_[HOSTNAME_MAX_LEN];
3590 strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3591 unsigned long length = hostname.length();
3592 inbind[0].buffer_type = MYSQL_TYPE_STRING;
3593 inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3594 inbind[0].buffer_length = length;
3595 inbind[0].length = &length;
3596
3597 // Subnet ID
3598 uint32_t subnet = subnet_id;
3599 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3600 inbind[1].buffer = reinterpret_cast<char*>(&subnet);
3601 inbind[1].is_unsigned = MLM_TRUE;
3602
3603 ConstHostCollection result;
3604 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6, inbind,
3605 ctx->host_ipv6_exchange_, result, false);
3606
3607 return (result);
3608}
3609
3612 size_t& /*source_index*/,
3613 uint64_t lower_host_id,
3614 const HostPageSize& page_size) const {
3615 // Get a context
3616 MySqlHostContextAlloc get_context(*impl_);
3617 MySqlHostContextPtr ctx = get_context.ctx_;
3618
3619 // Set up the WHERE clause value
3620 MYSQL_BIND inbind[3];
3621 memset(inbind, 0, sizeof(inbind));
3622
3623 // Bind subnet id
3624 uint32_t subnet = subnet_id;
3625 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3626 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3627 inbind[0].is_unsigned = MLM_TRUE;
3628
3629 // Bind lower host id
3630 uint32_t host_id = lower_host_id;
3631 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3632 inbind[1].buffer = reinterpret_cast<char*>(&host_id);
3633 inbind[1].is_unsigned = MLM_TRUE;
3634
3635 // Bind page size value
3636 uint32_t page_size_data = page_size.page_size_;
3637 inbind[2].buffer_type = MYSQL_TYPE_LONG;
3638 inbind[2].buffer = reinterpret_cast<char*>(&page_size_data);
3639 inbind[2].is_unsigned = MLM_TRUE;
3640
3641 ConstHostCollection result;
3642 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE, inbind,
3643 ctx->host_ipv4_exchange_, result, false);
3644
3645 return (result);
3646}
3647
3650 size_t& /*source_index*/,
3651 uint64_t lower_host_id,
3652 const HostPageSize& page_size) const {
3653 // Get a context
3654 MySqlHostContextAlloc get_context(*impl_);
3655 MySqlHostContextPtr ctx = get_context.ctx_;
3656
3657 // Set up the WHERE clause value
3658 MYSQL_BIND inbind[3];
3659 memset(inbind, 0, sizeof(inbind));
3660
3661 // Bind subnet id
3662 uint32_t subnet = subnet_id;
3663 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3664 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3665 inbind[0].is_unsigned = MLM_TRUE;
3666
3667 // Bind lower host id
3668 uint32_t host_id = lower_host_id;
3669 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3670 inbind[1].buffer = reinterpret_cast<char*>(&host_id);
3671 inbind[1].is_unsigned = MLM_TRUE;
3672
3673 // Bind page size value
3674 uint32_t page_size_data = page_size.page_size_;
3675 inbind[2].buffer_type = MYSQL_TYPE_LONG;
3676 inbind[2].buffer = reinterpret_cast<char*>(&page_size_data);
3677 inbind[2].is_unsigned = MLM_TRUE;
3678
3679 ConstHostCollection result;
3680 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE, inbind,
3681 ctx->host_ipv6_exchange_, result, false);
3682
3683 return (result);
3684}
3685
3687MySqlHostDataSource::getPage4(size_t& /*source_index*/,
3688 uint64_t lower_host_id,
3689 const HostPageSize& page_size) const {
3690 // Get a context
3691 MySqlHostContextAlloc get_context(*impl_);
3692 MySqlHostContextPtr ctx = get_context.ctx_;
3693
3694 // Set up the WHERE clause value
3695 MYSQL_BIND inbind[2];
3696 memset(inbind, 0, sizeof(inbind));
3697
3698 // Bind lower host id
3699 uint32_t host_id = lower_host_id;
3700 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3701 inbind[0].buffer = reinterpret_cast<char*>(&host_id);
3702 inbind[0].is_unsigned = MLM_TRUE;
3703
3704 // Bind page size value
3705 uint32_t page_size_data = page_size.page_size_;
3706 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3707 inbind[1].buffer = reinterpret_cast<char*>(&page_size_data);
3708 inbind[1].is_unsigned = MLM_TRUE;
3709
3710 ConstHostCollection result;
3711 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PAGE4, inbind,
3712 ctx->host_ipv4_exchange_, result, false);
3713
3714 return (result);
3715}
3716
3718MySqlHostDataSource::getPage6(size_t& /*source_index*/,
3719 uint64_t lower_host_id,
3720 const HostPageSize& page_size) const {
3721 // Get a context
3722 MySqlHostContextAlloc get_context(*impl_);
3723 MySqlHostContextPtr ctx = get_context.ctx_;
3724
3725 // Set up the WHERE clause value
3726 MYSQL_BIND inbind[2];
3727 memset(inbind, 0, sizeof(inbind));
3728
3729 // Bind lower host id
3730 uint32_t host_id = lower_host_id;
3731 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3732 inbind[0].buffer = reinterpret_cast<char*>(&host_id);
3733 inbind[0].is_unsigned = MLM_TRUE;
3734
3735 // Bind page size value
3736 uint32_t page_size_data = page_size.page_size_;
3737 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3738 inbind[1].buffer = reinterpret_cast<char*>(&page_size_data);
3739 inbind[1].is_unsigned = MLM_TRUE;
3740
3741 ConstHostCollection result;
3742 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PAGE6, inbind,
3743 ctx->host_ipv6_exchange_, result, false);
3744
3745 return (result);
3746}
3747
3750 // Get a context
3751 MySqlHostContextAlloc get_context(*impl_);
3752 MySqlHostContextPtr ctx = get_context.ctx_;
3753
3754 // Set up the WHERE clause value
3755 MYSQL_BIND inbind[1];
3756 memset(inbind, 0, sizeof(inbind));
3757
3758 uint32_t addr4 = address.toUint32();
3759 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3760 inbind[0].buffer = reinterpret_cast<char*>(&addr4);
3761 inbind[0].is_unsigned = MLM_TRUE;
3762
3763 ConstHostCollection result;
3764 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_ADDR, inbind,
3765 ctx->host_ipv4_exchange_, result, false);
3766
3767 return (result);
3768}
3769
3772 const Host::IdentifierType& identifier_type,
3773 const uint8_t* identifier_begin,
3774 const size_t identifier_len) const {
3775 // Get a context
3776 MySqlHostContextAlloc get_context(*impl_);
3777 MySqlHostContextPtr ctx = get_context.ctx_;
3778
3779 return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3781 ctx->host_ipv4_exchange_));
3782}
3783
3786 const asiolink::IOAddress& address) const {
3787 if (!address.isV4()) {
3788 isc_throw(BadValue, "MySqlHostDataSource::get4(id, address): "
3789 "wrong address type, address supplied is an IPv6 address");
3790 }
3791
3792 // Get a context
3793 MySqlHostContextAlloc get_context(*impl_);
3794 MySqlHostContextPtr ctx = get_context.ctx_;
3795
3796 // Set up the WHERE clause value
3797 MYSQL_BIND inbind[2];
3798 uint32_t subnet = subnet_id;
3799 memset(inbind, 0, sizeof(inbind));
3800 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3801 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3802 inbind[0].is_unsigned = MLM_TRUE;
3803
3804 uint32_t addr4 = address.toUint32();
3805 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3806 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3807 inbind[1].is_unsigned = MLM_TRUE;
3808
3809 ConstHostCollection collection;
3810 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
3811 ctx->host_ipv4_exchange_, collection, true);
3812
3813 // Return single record if present, else clear the host.
3814 ConstHostPtr result;
3815 if (!collection.empty()) {
3816 result = *collection.begin();
3817 }
3818
3819 return (result);
3820}
3821
3824 const asiolink::IOAddress& address) const {
3825 if (!address.isV4()) {
3826 isc_throw(BadValue, "MySqlHostDataSource::getAll4(id, address): "
3827 "wrong address type, address supplied is an IPv6 address");
3828 }
3829
3830 // Get a context
3831 MySqlHostContextAlloc get_context(*impl_);
3832 MySqlHostContextPtr ctx = get_context.ctx_;
3833
3834 // Set up the WHERE clause value
3835 MYSQL_BIND inbind[2];
3836 uint32_t subnet = subnet_id;
3837 memset(inbind, 0, sizeof(inbind));
3838 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3839 inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3840 inbind[0].is_unsigned = MLM_TRUE;
3841
3842 uint32_t addr4 = address.toUint32();
3843 inbind[1].buffer_type = MYSQL_TYPE_LONG;
3844 inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3845 inbind[1].is_unsigned = MLM_TRUE;
3846
3847 ConstHostCollection collection;
3848 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
3849 ctx->host_ipv4_exchange_, collection, false);
3850 return (collection);
3851}
3852
3855 const Host::IdentifierType& identifier_type,
3856 const uint8_t* identifier_begin,
3857 const size_t identifier_len) const {
3858 // Get a context
3859 MySqlHostContextAlloc get_context(*impl_);
3860 MySqlHostContextPtr ctx = get_context.ctx_;
3861
3862 return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3864 ctx->host_ipv6_exchange_));
3865}
3866
3869 const uint8_t prefix_len) const {
3870 if (!prefix.isV6()) {
3871 isc_throw(BadValue, "MySqlHostDataSource::get6(prefix, prefix_len): "
3872 "wrong address type, address supplied is an IPv4 address");
3873 }
3874
3875 // Get a context
3876 MySqlHostContextAlloc get_context(*impl_);
3877 MySqlHostContextPtr ctx = get_context.ctx_;
3878
3879 // Set up the WHERE clause value
3880 MYSQL_BIND inbind[2];
3881 memset(inbind, 0, sizeof(inbind));
3882
3883 std::vector<uint8_t>addr6 = prefix.toBytes();
3884 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
3885 isc_throw(DbOperationError, "get6() - prefix is not "
3886 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
3887 }
3888
3889 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
3890 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
3891 inbind[0].buffer = reinterpret_cast<char*>(&addr6[0]);
3892 inbind[0].buffer_length = isc::asiolink::V6ADDRESS_LEN;
3893 inbind[0].length = &addr6_length;
3894
3895 uint8_t tmp = prefix_len;
3896 inbind[1].buffer_type = MYSQL_TYPE_TINY;
3897 inbind[1].buffer = reinterpret_cast<char*>(&tmp);
3898 inbind[1].is_unsigned = MLM_TRUE;
3899
3900 ConstHostCollection collection;
3901 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PREFIX, inbind,
3902 ctx->host_ipv6_exchange_, collection, true);
3903
3904 // Return single record if present, else clear the host.
3905 ConstHostPtr result;
3906 if (!collection.empty()) {
3907 result = *collection.begin();
3908 }
3909
3910 return (result);
3911}
3912
3915 const asiolink::IOAddress& address) const {
3916 if (!address.isV6()) {
3917 isc_throw(BadValue, "MySqlHostDataSource::get6(id, address): "
3918 "wrong address type, address supplied is an IPv4 address");
3919 }
3920
3921 // Get a context
3922 MySqlHostContextAlloc get_context(*impl_);
3923 MySqlHostContextPtr ctx = get_context.ctx_;
3924
3925 // Set up the WHERE clause value
3926 MYSQL_BIND inbind[2];
3927 memset(inbind, 0, sizeof(inbind));
3928
3929 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3930 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3931 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3932 inbind[0].is_unsigned = MLM_TRUE;
3933
3934 std::vector<uint8_t>addr6 = address.toBytes();
3935 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
3936 isc_throw(DbOperationError, "get6() - address is not "
3937 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
3938 }
3939
3940 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
3941 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3942 inbind[1].buffer = reinterpret_cast<char*>(&addr6[0]);
3943 inbind[1].buffer_length = isc::asiolink::V6ADDRESS_LEN;
3944 inbind[1].length = &addr6_length;
3945
3946 ConstHostCollection collection;
3947 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
3948 ctx->host_ipv6_exchange_, collection, true);
3949
3950 // Return single record if present, else clear the host.
3951 ConstHostPtr result;
3952 if (!collection.empty()) {
3953 result = *collection.begin();
3954 }
3955
3956 return (result);
3957}
3958
3961 const asiolink::IOAddress& address) const {
3962 if (!address.isV6()) {
3963 isc_throw(BadValue, "MySqlHostDataSource::getAll6(id, address): "
3964 "wrong address type, address supplied is an IPv4 address");
3965 }
3966
3967 // Get a context
3968 MySqlHostContextAlloc get_context(*impl_);
3969 MySqlHostContextPtr ctx = get_context.ctx_;
3970
3971 // Set up the WHERE clause value
3972 MYSQL_BIND inbind[2];
3973 memset(inbind, 0, sizeof(inbind));
3974
3975 uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3976 inbind[0].buffer_type = MYSQL_TYPE_LONG;
3977 inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3978 inbind[0].is_unsigned = MLM_TRUE;
3979
3980 std::vector<uint8_t>addr6 = address.toBytes();
3981 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
3982 isc_throw(DbOperationError, "getAll6() - address is not "
3983 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
3984 }
3985
3986 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
3987 inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3988 inbind[1].buffer = reinterpret_cast<char*>(&addr6[0]);
3989 inbind[1].buffer_length = isc::asiolink::V6ADDRESS_LEN;
3990 inbind[1].length = &addr6_length;
3991
3992 ConstHostCollection collection;
3993 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
3994 ctx->host_ipv6_exchange_, collection, false);
3995 return (collection);
3996}
3997
4000 if (!address.isV6()) {
4001 isc_throw(BadValue, "MySqlHostDataSource::getAll6(address): "
4002 "wrong address type, address supplied is an IPv4 address");
4003 }
4004
4005 // Get a context
4006 MySqlHostContextAlloc get_context(*impl_);
4007 MySqlHostContextPtr ctx = get_context.ctx_;
4008
4009 // Set up the WHERE clause value
4010 MYSQL_BIND inbind[1];
4011 memset(inbind, 0, sizeof(inbind));
4012
4013 std::vector<uint8_t>addr6 = address.toBytes();
4014 if (addr6.size() != isc::asiolink::V6ADDRESS_LEN) {
4015 isc_throw(DbOperationError, "getAll6() - address is not "
4016 << isc::asiolink::V6ADDRESS_LEN << " bytes long");
4017 }
4018
4019 unsigned long addr6_length = isc::asiolink::V6ADDRESS_LEN;
4020 inbind[0].buffer_type = MYSQL_TYPE_BLOB;
4021 inbind[0].buffer = reinterpret_cast<char*>(&addr6[0]);
4022 inbind[0].buffer_length = isc::asiolink::V6ADDRESS_LEN;
4023 inbind[0].length = &addr6_length;
4024
4025 ConstHostCollection collection;
4026 impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_ADDR6, inbind,
4027 ctx->host_ipv6_exchange_, collection, false);
4028 return (collection);
4029}
4030
4031void
4033 // Get a context.
4034 MySqlHostContextAlloc const context(*impl_);
4035 MySqlHostContextPtr ctx(context.ctx_);
4036
4037 // If operating in read-only mode, throw exception.
4038 impl_->checkReadOnly(ctx);
4039
4040 // Initiate MySQL transaction as we will have to make multiple queries
4041 // to update host information into multiple tables. If that fails on
4042 // any stage, the transaction will be rolled back by the destructor of
4043 // the MySqlTransaction class.
4044 MySqlTransaction transaction(ctx->conn_);
4045
4046 // As much as having dedicated prepared statements for updating tables would be consistent with
4047 // the implementation of other commands, it's difficult if not impossible to cover all cases for
4048 // updating the host to exactly as is described in the command, which may involve inserts and
4049 // deletes alongside updates. So let's delete and add. The delete cascades into all tables. The
4050 // add explicitly adds into all tables.
4052
4053 // Everything went fine, so explicitly commit the transaction.
4054 transaction.commit();
4055}
4056
4057// Miscellaneous database methods.
4058
4059std::string
4061 std::string name = "";
4062 // Get a context
4063 MySqlHostContextAlloc get_context(*impl_);
4064 MySqlHostContextPtr ctx = get_context.ctx_;
4065
4066 try {
4067 name = ctx->conn_.getParameter("name");
4068 } catch (...) {
4069 // Return an empty name
4070 }
4071 return (name);
4072}
4073
4074std::string
4076 return (std::string("Host data source that stores host information"
4077 "in MySQL database"));
4078}
4079
4080std::pair<uint32_t, uint32_t>
4081MySqlHostDataSource::getVersion(const std::string& timer_name) const {
4082 return(impl_->getVersion(timer_name));
4083}
4084
4085void
4087 // Get a context
4088 MySqlHostContextAlloc get_context(*impl_);
4089 MySqlHostContextPtr ctx = get_context.ctx_;
4090
4091 // If operating in read-only mode, throw exception.
4092 impl_->checkReadOnly(ctx);
4093 if (ctx->conn_.isTransactionStarted()) {
4094 ctx->conn_.commit();
4095 }
4096}
4097
4098void
4100 // Get a context
4101 MySqlHostContextAlloc get_context(*impl_);
4102 MySqlHostContextPtr ctx = get_context.ctx_;
4103
4104 // If operating in read-only mode, throw exception.
4105 impl_->checkReadOnly(ctx);
4106 if (ctx->conn_.isTransactionStarted()) {
4107 ctx->conn_.rollback();
4108 }
4109}
4110
4111bool
4113 impl_->ip_reservations_unique_ = unique;
4114 return (true);
4115}
4116
4117bool
4119 return (impl_->unusable_);
4120}
4121
4122} // namespace dhcp
4123} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
static ElementPtr fromJSON(const std::string &in, bool preproc=false)
These functions will parse the given string (JSON) representation of a compound element.
Definition data.cc:798
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition data.h:49
Data is truncated.
static bool invokeDbLostCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's lost connectivity callback.
static bool invokeDbFailedCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restore failed connectivity callback.
static ParameterMap parse(const std::string &dbaccess)
Parse database access string.
static bool invokeDbRecoveredCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restored connectivity callback.
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Exception thrown on failure to execute a database function.
Database duplicate entry error.
Multiple lease records found where one expected.
Common MySQL Connector Pool.
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())
Get the schema version.
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...
Fetch and Release MySQL Results.
RAII object representing MySQL transaction.
Attempt to modify data in read-only database.
Authentication keys.
Definition host.h:75
virtual void update(HostPtr const &host)
Attempts to update an existing host entry.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:25
static constexpr size_t MAX_CLIENT_ID_LEN
Maximum size of a client ID.
Definition duid.h:235
static bool delBackend(const std::string &db_type)
Delete an alternate host backend (aka host data source).
Definition host_mgr.cc:62
static void addBackend(const std::string &access)
Add an alternate host backend (aka host data source).
Definition host_mgr.cc:57
Wraps value holding size of the page with host reservations.
Represents a device with IPv4 and/or IPv6 reservations.
Definition host.h:297
IdentifierType
Type of the host identifier.
Definition host.h:307
static const IdentifierType LAST_IDENTIFIER_TYPE
Constant pointing to the last identifier of the IdentifierType enumeration.
Definition host.h:317
IPv6 reservation for a host.
Definition host.h:161
Type
Type of the reservation.
Definition host.h:167
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition libdhcp++.cc:126
static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, const uint16_t code)
Returns vendor option definition for a given vendor-id and code.
Definition libdhcp++.cc:168
static uint32_t optionSpaceToVendorId(const std::string &option_space)
Converts option space name to vendor id.
static OptionDefinitionPtr getRuntimeOptionDef(const std::string &space, const uint16_t code)
Returns runtime (non-standard) option definition by space and option code.
Definition libdhcp++.cc:189
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition libdhcp++.cc:247
std::vector< MySqlHostContextPtr > pool_
The vector of available contexts.
std::mutex mutex_
The mutex to protect pool access.
MySqlHostContext(const DatabaseConnection::ParameterMap &parameters, IOServiceAccessorPtr io_service_accessor, db::DbCallback db_reconnect_callback)
Constructor.
boost::shared_ptr< MySqlHostIPv6Exchange > host_ipv46_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
boost::shared_ptr< MySqlHostIPv6Exchange > host_ipv6_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
boost::shared_ptr< MySqlIPv6ReservationExchange > host_ipv6_reservation_exchange_
Pointer to an object representing an exchange which can be used to insert new IPv6 reservation.
boost::shared_ptr< MySqlHostWithOptionsExchange > host_ipv4_exchange_
The exchange objects are used for transfer of data to/from the database.
boost::shared_ptr< MySqlOptionExchange > host_option_exchange_
Pointer to an object representing an exchange which can be used to insert DHCPv4 or DHCPv6 option int...
MySqlConnection conn_
MySQL connection.
bool is_readonly_
Indicates if the database is opened in read only mode.
Implementation of the MySqlHostDataSource.
void checkReadOnly(MySqlHostContextPtr &ctx) const
Throws exception if database is read only.
void checkError(MySqlHostContextPtr &ctx, const int status, const StatementIndex index, const char *what) const
Check Error and Throw Exception.
std::string timer_name_
Timer name used to register database reconnect timer.
DatabaseConnection::ParameterMap parameters_
The parameters.
MySqlHostContextPtr createContext() const
Create a new context.
bool delStatement(MySqlHostContextPtr &ctx, StatementIndex stindex, MYSQL_BIND *bind)
Executes statements that delete records.
MySqlHostDataSourceImpl(const DatabaseConnection::ParameterMap &parameters)
Constructor.
ConstHostPtr getHost(MySqlHostContextPtr &ctx, const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, StatementIndex stindex, boost::shared_ptr< MySqlHostExchange > exchange) const
Retrieves a host by subnet and client's unique identifier.
void addStatement(MySqlHostContextPtr &ctx, MySqlHostDataSourceImpl::StatementIndex stindex, std::vector< MYSQL_BIND > &bind)
Executes statements which inserts a row into one of the tables.
std::pair< uint32_t, uint32_t > getVersion(const std::string &timer_name=std::string()) const
Returns backend version.
void getHostCollection(MySqlHostContextPtr &ctx, StatementIndex stindex, MYSQL_BIND *bind, boost::shared_ptr< MySqlHostExchange > exchange, ConstHostCollection &result, bool single) const
Creates collection of Host objects with associated information such as IPv6 reservations and/or DHCP ...
bool ip_reservations_unique_
Holds the setting whether the IP reservations must be unique or may be non-unique.
void addOptions(MySqlHostContextPtr &ctx, const StatementIndex &stindex, const ConstCfgOptionPtr &options_cfg, const uint64_t host_id)
Inserts multiple options into the database.
MySqlHostContextPoolPtr pool_
The pool of contexts.
void addOption(MySqlHostContextPtr &ctx, const MySqlHostDataSourceImpl::StatementIndex &stindex, const OptionDescriptor &opt_desc, const std::string &opt_space, const Optional< SubnetID > &subnet_id, const HostID &host_id)
Inserts a single DHCP option into the database.
bool unusable_
Indicates if there is at least one connection that can no longer be used for normal operations.
void addResv(MySqlHostContextPtr &ctx, const IPv6Resrv &resv, const HostID &id)
Inserts IPv6 Reservation into ipv6_reservation table.
static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl)
Attempts to reconnect the server to the host DB backend manager.
static const StatementIndex WRITE_STMTS_BEGIN
Index of first statement performing write to the database.
MySqlHostContextAlloc(MySqlHostDataSourceImpl &mgr)
Constructor.
virtual ConstHostCollection getAll(const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Return all hosts connected to any subnet for which reservations have been made using a specified iden...
virtual ConstHostPtr get6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv6 subnet.
virtual std::pair< uint32_t, uint32_t > getVersion(const std::string &timer_name=std::string()) const
Returns backend version.
virtual void commit()
Commit Transactions.
virtual bool del(const SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete hosts by (subnet-id, address)
virtual ConstHostCollection getAll4(const SubnetID &subnet_id) const
Return all hosts in a DHCPv4 subnet.
virtual ConstHostCollection getAll6(const SubnetID &subnet_id) const
Return all hosts in a DHCPv6 subnet.
virtual std::string getDescription() const
Returns description of the backend.
MySqlHostDataSource(const db::DatabaseConnection::ParameterMap &parameters)
Constructor.
virtual ~MySqlHostDataSource()
Virtual destructor.
virtual ConstHostCollection getAllbyHostname(const std::string &hostname) const
Return all hosts with a hostname.
virtual isc::db::DatabaseConnection::ParameterMap getParameters() const
Return backend parameters.
void update(HostPtr const &host)
Implements BaseHostDataSource::update() for MySQL.
virtual bool del6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Attempts to delete a host by (subnet6-id, identifier type, identifier)
virtual ConstHostCollection getPage6(const SubnetID &subnet_id, size_t &source_index, uint64_t lower_host_id, const HostPageSize &page_size) const
Returns range of hosts in a DHCPv6 subnet.
virtual ConstHostCollection getAllbyHostname6(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv6 subnet.
virtual bool del4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Attempts to delete a host by (subnet4-id, identifier type, identifier)
virtual ConstHostCollection getPage4(const SubnetID &subnet_id, size_t &source_index, uint64_t lower_host_id, const HostPageSize &page_size) const
Returns range of hosts in a DHCPv4 subnet.
virtual bool isUnusable()
Flag which indicates if the host manager has at least one unusable connection.
virtual ConstHostCollection getAllbyHostname4(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv4 subnet.
virtual void rollback()
Rollback Transactions.
virtual std::string getName() const
Returns backend name.
virtual void add(const HostPtr &host)
Adds a new host to the collection.
virtual ConstHostPtr get4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv4 subnet.
virtual bool setIPReservationsUnique(const bool unique)
Controls whether IP reservations are unique or non-unique.
Option descriptor.
Definition cfg_option.h:47
Universe
defines option universe DHCPv4 or DHCPv6
Definition option.h:83
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition timer_mgr.cc:446
RAII class creating a critical section.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition buffer.h:343
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
const my_bool MLM_FALSE
MySQL false value.
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
@ error
Definition db_log.h:118
const my_bool MLM_TRUE
MySQL true value.
bool my_bool
my_bool type in MySQL 8.x.
const int MLM_MYSQL_FETCH_FAILURE
MySQL fetch failure code.
const int MLM_MYSQL_FETCH_SUCCESS
check for bool size
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.
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition dhcpsrv_log.h:56
const size_t OPTION_FORMATTED_VALUE_MAX_LEN
Maximum length of option value specified in textual format.
Definition host.h:48
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_GET_VERSION
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition cfg_option.h:803
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition host.h:807
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition host.h:813
const isc::log::MessageID DHCPSRV_MYSQL_NO_TLS
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
const size_t CLIENT_CLASSES_MAX_LEN
Maximum length of classes stored in a dhcp4/6_client_classes columns.
Definition host.h:36
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition host.h:243
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition dhcpsrv_log.h:38
const size_t TEXT_AUTH_KEY_LEN
Maximum length of authentication keys (coded in hexadecimal).
Definition host.h:66
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_FAILED
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition subnet_id.h:25
uint64_t HostID
HostID (used only when storing in MySQL or PostgreSQL backends)
Definition host.h:69
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition cfg_option.h:303
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition host.h:810
boost::shared_ptr< MySqlHostContextPool > MySqlHostContextPoolPtr
Type of pointers to context pools.
const size_t HOSTNAME_MAX_LEN
Maximum length of the hostname stored in DNS.
Definition host.h:42
boost::shared_ptr< MySqlHostContext > MySqlHostContextPtr
Type of pointers to contexts.
const size_t USER_CONTEXT_MAX_LEN
Maximum length of user context.
Definition host.h:54
const size_t OPTION_VALUE_MAX_LEN
Maximum length of option value.
Definition host.h:45
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition option.h:24
const size_t SERVER_HOSTNAME_MAX_LEN
Maximum length of the server hostname.
Definition host.h:57
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_READONLY
const size_t BOOT_FILE_NAME_MAX_LEN
Maximum length of the boot file name.
Definition host.h:60
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition dhcpsrv_log.h:26
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_ATTEMPT_SCHEDULE
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_ATTEMPT_FAILED
const isc::log::MessageID DHCPSRV_MYSQL_TLS_CIPHER
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition cfg_option.h:806
const size_t OPTION_SPACE_MAX_LEN
Maximum length of option space name.
Definition host.h:51
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
Defines the logger used by the top-level component of kea-lfc.
uint16_t code_
#define DHCP4_OPTION_SPACE
global std option spaces
#define DHCP6_OPTION_SPACE