Kea  2.3.1-git
mysql_host_data_source.cc
Go to the documentation of this file.
1 // Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <asiolink/io_service.h>
10 #include <database/db_exceptions.h>
11 #include <dhcp/libdhcp++.h>
12 #include <dhcp/option.h>
13 #include <dhcp/option_definition.h>
14 #include <dhcp/option_space.h>
15 #include <dhcpsrv/cfg_db_access.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/pointer_cast.hpp>
30 #include <boost/static_assert.hpp>
31 
32 #include <mysql.h>
33 #include <mysqld_error.h>
34 #include <stdint.h>
35 
36 #include <mutex>
37 #include <string>
38 
39 using namespace isc;
40 using namespace isc::asiolink;
41 using namespace isc::db;
42 using namespace isc::dhcp;
43 using namespace isc::util;
44 using namespace isc::data;
45 using namespace std;
46 
47 namespace {
48 
53 const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
54 
83 class MySqlHostExchange {
84 private:
85 
87  static const size_t HOST_COLUMNS = 14;
88 
89 public:
90 
97  MySqlHostExchange(const size_t additional_columns_num = 0)
98  : columns_num_(HOST_COLUMNS + additional_columns_num),
99  bind_(columns_num_), columns_(columns_num_),
100  error_(columns_num_, MLM_FALSE), host_id_(0),
101  dhcp_identifier_length_(0), dhcp_identifier_type_(0),
102  dhcp4_subnet_id_(SUBNET_ID_UNUSED),
103  dhcp6_subnet_id_(SUBNET_ID_UNUSED), ipv4_address_(0),
104  hostname_length_(0), dhcp4_client_classes_length_(0),
105  dhcp6_client_classes_length_(0),
106  user_context_length_(0),
107  dhcp4_next_server_(0),
108  dhcp4_server_hostname_length_(0),
109  dhcp4_boot_file_name_length_(0),
110  auth_key_length_(0),
111  dhcp4_subnet_id_null_(MLM_FALSE),
112  dhcp6_subnet_id_null_(MLM_FALSE),
113  ipv4_address_null_(MLM_FALSE), hostname_null_(MLM_FALSE),
114  dhcp4_client_classes_null_(MLM_FALSE),
115  dhcp6_client_classes_null_(MLM_FALSE),
116  user_context_null_(MLM_FALSE),
117  dhcp4_next_server_null_(MLM_FALSE),
118  dhcp4_server_hostname_null_(MLM_FALSE),
119  dhcp4_boot_file_name_null_(MLM_FALSE),
120  auth_key_null_(MLM_FALSE) {
121 
122  // Fill arrays with 0 so as they don't include any garbage.
123  memset(dhcp_identifier_buffer_, 0, sizeof(dhcp_identifier_buffer_));
124  memset(hostname_, 0, sizeof(hostname_));
125  memset(dhcp4_client_classes_, 0, sizeof(dhcp4_client_classes_));
126  memset(dhcp6_client_classes_, 0, sizeof(dhcp6_client_classes_));
127  memset(user_context_, 0, sizeof(user_context_));
128  memset(dhcp4_server_hostname_, 0, sizeof(dhcp4_server_hostname_));
129  memset(dhcp4_boot_file_name_, 0, sizeof(dhcp4_boot_file_name_));
130  memset(auth_key_, 0, sizeof(auth_key_));
131 
132  // Set the column names for use by this class. This only comprises
133  // names used by the MySqlHostExchange class. Derived classes will
134  // need to set names for the columns they use.
135  columns_[0] = "host_id";
136  columns_[1] = "dhcp_identifier";
137  columns_[2] = "dhcp_identifier_type";
138  columns_[3] = "dhcp4_subnet_id";
139  columns_[4] = "dhcp6_subnet_id";
140  columns_[5] = "ipv4_address";
141  columns_[6] = "hostname";
142  columns_[7] = "dhcp4_client_classes";
143  columns_[8] = "dhcp6_client_classes";
144  columns_[9] = "user_context";
145  columns_[10] = "dhcp4_next_server";
146  columns_[11] = "dhcp4_server_hostname";
147  columns_[12] = "dhcp4_boot_file_name";
148  columns_[13] = "auth_key";
149 
150  BOOST_STATIC_ASSERT(13 < HOST_COLUMNS);
151  };
152 
154  virtual ~MySqlHostExchange() {
155  }
156 
171  size_t findAvailColumn() const {
172  std::vector<std::string>::const_iterator empty_column =
173  std::find(columns_.begin(), columns_.end(), std::string());
174  return (std::distance(columns_.begin(), empty_column));
175  }
176 
180  uint64_t getHostId() const {
181  return (host_id_);
182  };
183 
194  static void setErrorIndicators(std::vector<MYSQL_BIND>& bind,
195  std::vector<my_bools>& error) {
196  for (size_t i = 0; i < error.size(); ++i) {
197  error[i] = MLM_FALSE;
198  bind[i].error = reinterpret_cast<my_bool*>(&error[i]);
199  }
200  };
201 
215  static std::string getColumnsInError(std::vector<my_bools>& error,
216  const std::vector<std::string>& names) {
217  std::string result = "";
218 
219  // Accumulate list of column names
220  for (size_t i = 0; i < names.size(); ++i) {
221  if (error[i] == MLM_TRUE) {
222  if (!result.empty()) {
223  result += ", ";
224  }
225  result += names[i];
226  }
227  }
228 
229  if (result.empty()) {
230  result = "(None)";
231  }
232 
233  return (result);
234  };
235 
248  std::vector<MYSQL_BIND> createBindForSend(const HostPtr& host, const bool unique_ip) {
249  // Store host object to ensure it remains valid.
250  host_ = host;
251 
252  // Initialize prior to constructing the array of MYSQL_BIND structures.
253  // It sets all fields, including is_null, to zero, so we need to set
254  // is_null only if it should be true. This gives up minor performance
255  // benefit while being safe approach.
256  memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
257 
258  // Set up the structures for the various components of the host structure.
259 
260  try {
261  // host_id : INT UNSIGNED NOT NULL
262  // The host_id is auto_incremented by MySQL database,
263  // so we need to pass the NULL value
264  host_id_ = 0;
265  bind_[0].buffer_type = MYSQL_TYPE_LONG;
266  bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
267  bind_[0].is_unsigned = MLM_TRUE;
268 
269  // dhcp_identifier : VARBINARY(128) NOT NULL
270  dhcp_identifier_length_ = host->getIdentifier().size();
271  memcpy(static_cast<void*>(dhcp_identifier_buffer_),
272  &(host->getIdentifier())[0],
273  host->getIdentifier().size());
274 
275  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
276  bind_[1].buffer = dhcp_identifier_buffer_;
277  bind_[1].buffer_length = dhcp_identifier_length_;
278  bind_[1].length = &dhcp_identifier_length_;
279 
280  // dhcp_identifier_type : TINYINT NOT NULL
281  dhcp_identifier_type_ = static_cast<uint8_t>(host->getIdentifierType());
282  bind_[2].buffer_type = MYSQL_TYPE_TINY;
283  bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
284  bind_[2].is_unsigned = MLM_TRUE;
285 
286  // dhcp4_subnet_id : INT UNSIGNED NULL
287  // Can't take an address of intermediate object, so let's store it
288  // in dhcp4_subnet_id_
289  dhcp4_subnet_id_ = host->getIPv4SubnetID();
290  dhcp4_subnet_id_null_ = host->getIPv4SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
291  bind_[3].buffer_type = MYSQL_TYPE_LONG;
292  bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
293  bind_[3].is_unsigned = MLM_TRUE;
294  bind_[3].is_null = &dhcp4_subnet_id_null_;
295 
296  // dhcp6_subnet_id : INT UNSIGNED NULL
297  // Can't take an address of intermediate object, so let's store it
298  // in dhcp6_subnet_id_
299  dhcp6_subnet_id_ = host->getIPv6SubnetID();
300  dhcp6_subnet_id_null_ = host->getIPv6SubnetID() == SUBNET_ID_UNUSED ? MLM_TRUE : MLM_FALSE;
301  bind_[4].buffer_type = MYSQL_TYPE_LONG;
302  bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
303  bind_[4].is_unsigned = MLM_TRUE;
304  bind_[4].is_null = &dhcp6_subnet_id_null_;
305 
306  // ipv4_address : INT UNSIGNED NULL
307  // The address in the Host structure is an IOAddress object. Convert
308  // this to an integer for storage.
309  ipv4_address_ = host->getIPv4Reservation().toUint32();
310  ipv4_address_null_ = ipv4_address_ == 0 ? MLM_TRUE : MLM_FALSE;
311  bind_[5].buffer_type = MYSQL_TYPE_LONG;
312  bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
313  bind_[5].is_unsigned = MLM_TRUE;
314  bind_[5].is_null = &ipv4_address_null_;
315 
316  // hostname : VARCHAR(255) NULL
317  strncpy(hostname_, host->getHostname().c_str(), HOSTNAME_MAX_LEN - 1);
318  hostname_length_ = host->getHostname().length();
319  bind_[6].buffer_type = MYSQL_TYPE_STRING;
320  bind_[6].buffer = reinterpret_cast<char*>(hostname_);
321  bind_[6].buffer_length = hostname_length_;
322 
323  // dhcp4_client_classes : VARCHAR(255) NULL
324  bind_[7].buffer_type = MYSQL_TYPE_STRING;
325  // Override default separator to not include space after comma.
326  string classes4_txt = host->getClientClasses4().toText(",");
327  strncpy(dhcp4_client_classes_, classes4_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
328  bind_[7].buffer = dhcp4_client_classes_;
329  bind_[7].buffer_length = classes4_txt.length();
330 
331  // dhcp6_client_classes : VARCHAR(255) NULL
332  bind_[8].buffer_type = MYSQL_TYPE_STRING;
333  // Override default separator to not include space after comma.
334  string classes6_txt = host->getClientClasses6().toText(",");
335  strncpy(dhcp6_client_classes_, classes6_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
336  bind_[8].buffer = dhcp6_client_classes_;
337  bind_[8].buffer_length = classes6_txt.length();
338 
339  // user_context : TEXT NULL
340  ConstElementPtr ctx = host->getContext();
341  if (ctx) {
342  bind_[9].buffer_type = MYSQL_TYPE_STRING;
343  string ctx_txt = ctx->str();
344  strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
345  bind_[9].buffer = user_context_;
346  bind_[9].buffer_length = ctx_txt.length();
347  } else {
348  bind_[9].buffer_type = MYSQL_TYPE_NULL;
349  }
350 
351  // ipv4_address : INT UNSIGNED NULL
352  // The address in the Host structure is an IOAddress object. Convert
353  // this to an integer for storage.
354  dhcp4_next_server_ = host->getNextServer().toUint32();
355  bind_[10].buffer_type = MYSQL_TYPE_LONG;
356  bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
357  bind_[10].is_unsigned = MLM_TRUE;
358  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
359  // reasons, see memset() above
360 
361  // dhcp4_server_hostname
362  bind_[11].buffer_type = MYSQL_TYPE_STRING;
363  std::string server_hostname = host->getServerHostname();
364  strncpy(dhcp4_server_hostname_, server_hostname.c_str(),
366  bind_[11].buffer = dhcp4_server_hostname_;
367  bind_[11].buffer_length = server_hostname.length();
368 
369  // dhcp4_boot_file_name
370  bind_[12].buffer_type = MYSQL_TYPE_STRING;
371  std::string boot_file_name = host->getBootFileName();
372  strncpy(dhcp4_boot_file_name_, boot_file_name.c_str(),
374  bind_[12].buffer = dhcp4_boot_file_name_;
375  bind_[12].buffer_length = boot_file_name.length();
376 
377  // auth key
378  bind_[13].buffer_type = MYSQL_TYPE_STRING;
379  std::string auth_key = host->getKey().toText();
380  std::strncpy(auth_key_, auth_key.c_str(), TEXT_AUTH_KEY_LEN - 1);
381  auth_key_null_ = auth_key.empty() ? MLM_TRUE : MLM_FALSE;
382  bind_[13].buffer = auth_key_;
383  bind_[13].buffer_length = auth_key.length();
384 
385  } catch (const std::exception& ex) {
387  "Could not create bind array from Host: "
388  << host->getHostname() << ", reason: " << ex.what());
389  }
390 
391  // Add the data to the vector.
392  std::vector<MYSQL_BIND> vec(bind_.begin(), bind_.begin() + HOST_COLUMNS);
393 
394  // When checking whether the IP is unique we need to bind the IPv4 address
395  // at the end of the query as it has additional binding for the IPv4
396  // address.
397  if (unique_ip) {
398  vec.push_back(bind_[5]); // ipv4_address
399  vec.push_back(bind_[3]); // subnet_id
400  }
401  return (vec);
402  };
403 
411  virtual std::vector<MYSQL_BIND> createBindForReceive() {
412  // Initialize MYSQL_BIND array.
413  // It sets all fields, including is_null, to zero, so we need to set
414  // is_null only if it should be true. This gives up minor performance
415  // benefit while being safe approach. For improved readability, the
416  // code that explicitly sets is_null is there, but is commented out.
417  // This also takes care of setting bind_[X].is_null to MLM_FALSE.
418  memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
419 
420  // host_id : INT UNSIGNED NOT NULL
421  bind_[0].buffer_type = MYSQL_TYPE_LONG;
422  bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
423  bind_[0].is_unsigned = MLM_TRUE;
424 
425  // dhcp_identifier : VARBINARY(128) NOT NULL
426  dhcp_identifier_length_ = sizeof(dhcp_identifier_buffer_);
427  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
428  bind_[1].buffer = reinterpret_cast<char*>(dhcp_identifier_buffer_);
429  bind_[1].buffer_length = dhcp_identifier_length_;
430  bind_[1].length = &dhcp_identifier_length_;
431 
432  // dhcp_identifier_type : TINYINT NOT NULL
433  bind_[2].buffer_type = MYSQL_TYPE_TINY;
434  bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
435  bind_[2].is_unsigned = MLM_TRUE;
436 
437  // dhcp4_subnet_id : INT UNSIGNED NULL
438  dhcp4_subnet_id_null_ = MLM_FALSE;
439  bind_[3].buffer_type = MYSQL_TYPE_LONG;
440  bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
441  bind_[3].is_unsigned = MLM_TRUE;
442  bind_[3].is_null = &dhcp4_subnet_id_null_;
443 
444  // dhcp6_subnet_id : INT UNSIGNED NULL
445  dhcp6_subnet_id_null_ = MLM_FALSE;
446  bind_[4].buffer_type = MYSQL_TYPE_LONG;
447  bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
448  bind_[4].is_unsigned = MLM_TRUE;
449  bind_[4].is_null = &dhcp6_subnet_id_null_;
450 
451  // ipv4_address : INT UNSIGNED NULL
452  ipv4_address_null_ = MLM_FALSE;
453  bind_[5].buffer_type = MYSQL_TYPE_LONG;
454  bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
455  bind_[5].is_unsigned = MLM_TRUE;
456  bind_[5].is_null = &ipv4_address_null_;
457 
458  // hostname : VARCHAR(255) NULL
459  hostname_null_ = MLM_FALSE;
460  hostname_length_ = sizeof(hostname_);
461  bind_[6].buffer_type = MYSQL_TYPE_STRING;
462  bind_[6].buffer = reinterpret_cast<char*>(hostname_);
463  bind_[6].buffer_length = hostname_length_;
464  bind_[6].length = &hostname_length_;
465  bind_[6].is_null = &hostname_null_;
466 
467  // dhcp4_client_classes : VARCHAR(255) NULL
468  dhcp4_client_classes_null_ = MLM_FALSE;
469  dhcp4_client_classes_length_ = sizeof(dhcp4_client_classes_);
470  bind_[7].buffer_type = MYSQL_TYPE_STRING;
471  bind_[7].buffer = reinterpret_cast<char*>(dhcp4_client_classes_);
472  bind_[7].buffer_length = dhcp4_client_classes_length_;
473  bind_[7].length = &dhcp4_client_classes_length_;
474  bind_[7].is_null = &dhcp4_client_classes_null_;
475 
476  // dhcp6_client_classes : VARCHAR(255) NULL
477  dhcp6_client_classes_null_ = MLM_FALSE;
478  dhcp6_client_classes_length_ = sizeof(dhcp6_client_classes_);
479  bind_[8].buffer_type = MYSQL_TYPE_STRING;
480  bind_[8].buffer = reinterpret_cast<char*>(dhcp6_client_classes_);
481  bind_[8].buffer_length = dhcp6_client_classes_length_;
482  bind_[8].length = &dhcp6_client_classes_length_;
483  bind_[8].is_null = &dhcp6_client_classes_null_;
484 
485  // user_context : TEXT NULL
486  user_context_null_ = MLM_FALSE;
487  user_context_length_ = sizeof(user_context_);
488  bind_[9].buffer_type = MYSQL_TYPE_STRING;
489  bind_[9].buffer = reinterpret_cast<char*>(user_context_);
490  bind_[9].buffer_length = user_context_length_;
491  bind_[9].length = &user_context_length_;
492  bind_[9].is_null = &user_context_null_;
493 
494  // dhcp4_next_server
495  dhcp4_next_server_null_ = MLM_FALSE;
496  bind_[10].buffer_type = MYSQL_TYPE_LONG;
497  bind_[10].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
498  bind_[10].is_unsigned = MLM_TRUE;
499  bind_[10].is_null = &dhcp4_next_server_null_;
500 
501  // dhcp4_server_hostname
502  dhcp4_server_hostname_null_ = MLM_FALSE;
503  dhcp4_server_hostname_length_ = sizeof(dhcp4_server_hostname_);
504  bind_[11].buffer_type = MYSQL_TYPE_STRING;
505  bind_[11].buffer = reinterpret_cast<char*>(dhcp4_server_hostname_);
506  bind_[11].buffer_length = dhcp4_server_hostname_length_;
507  bind_[11].length = &dhcp4_server_hostname_length_;
508  bind_[11].is_null = &dhcp4_server_hostname_null_;
509 
510  // dhcp4_boot_file_name
511  dhcp4_boot_file_name_null_ = MLM_FALSE;
512  dhcp4_boot_file_name_length_ = sizeof(dhcp4_boot_file_name_);
513  bind_[12].buffer_type = MYSQL_TYPE_STRING;
514  bind_[12].buffer = reinterpret_cast<char*>(dhcp4_boot_file_name_);
515  bind_[12].buffer_length = dhcp4_boot_file_name_length_;
516  bind_[12].length = &dhcp4_boot_file_name_length_;
517  bind_[12].is_null = &dhcp4_boot_file_name_null_;
518 
519  // auth_key_
520  auth_key_null_ = MLM_FALSE;
521  auth_key_length_ = sizeof(auth_key_);
522  bind_[13].buffer_type = MYSQL_TYPE_STRING;
523  bind_[13].buffer = reinterpret_cast<char*>(auth_key_);
524  bind_[13].buffer_length = auth_key_length_;
525  bind_[13].length = &auth_key_length_;
526  bind_[13].is_null = &auth_key_null_;
527 
528  // Add the error flags
529  setErrorIndicators(bind_, error_);
530 
531  // Add the data to the vector. Note the end element is one after the
532  // end of the array.
533  return (bind_);
534  };
535 
544  HostPtr retrieveHost() {
545  // Check if the identifier stored in the database is correct.
546  if (dhcp_identifier_type_ > MAX_IDENTIFIER_TYPE) {
547  isc_throw(BadValue, "invalid dhcp identifier type returned: "
548  << static_cast<int>(dhcp_identifier_type_));
549  }
550  // Set the dhcp identifier type in a variable of the appropriate
551  // data type.
552  Host::IdentifierType type =
553  static_cast<Host::IdentifierType>(dhcp_identifier_type_);
554 
555  // Set DHCPv4 subnet ID to the value returned. If NULL returned,
556  // set to 0.
557  SubnetID ipv4_subnet_id(SUBNET_ID_UNUSED);
558  if (dhcp4_subnet_id_null_ == MLM_FALSE) {
559  ipv4_subnet_id = static_cast<SubnetID>(dhcp4_subnet_id_);
560  }
561 
562  // Set DHCPv6 subnet ID to the value returned. If NULL returned,
563  // set to 0.
564  SubnetID ipv6_subnet_id(SUBNET_ID_UNUSED);
565  if (dhcp6_subnet_id_null_ == MLM_FALSE) {
566  ipv6_subnet_id = static_cast<SubnetID>(dhcp6_subnet_id_);
567  }
568 
569  // Set IPv4 address reservation if it was given, if not, set IPv4 zero
570  // address
571  asiolink::IOAddress ipv4_reservation = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
572  if (ipv4_address_null_ == MLM_FALSE) {
573  ipv4_reservation = asiolink::IOAddress(ipv4_address_);
574  }
575 
576  // Set hostname if non NULL value returned. Otherwise, leave an
577  // empty string.
578  std::string hostname;
579  if (hostname_null_ == MLM_FALSE) {
580  hostname = std::string(hostname_, hostname_length_);
581  }
582 
583  // Set DHCPv4 client classes if non NULL value returned.
584  std::string dhcp4_client_classes;
585  if (dhcp4_client_classes_null_ == MLM_FALSE) {
586  dhcp4_client_classes = std::string(dhcp4_client_classes_,
587  dhcp4_client_classes_length_);
588  }
589 
590  // Set DHCPv6 client classes if non NULL value returned.
591  std::string dhcp6_client_classes;
592  if (dhcp6_client_classes_null_ == MLM_FALSE) {
593  dhcp6_client_classes = std::string(dhcp6_client_classes_,
594  dhcp6_client_classes_length_);
595  }
596 
597  // Convert user_context to string as well.
598  std::string user_context;
599  if (user_context_null_ == MLM_FALSE) {
600  user_context_[user_context_length_] = '\0';
601  user_context.assign(user_context_);
602  }
603 
604  // Set next server value (siaddr) if non NULL value returned.
605  asiolink::IOAddress next_server = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
606  if (dhcp4_next_server_null_ == MLM_FALSE) {
607  next_server = asiolink::IOAddress(dhcp4_next_server_);
608  }
609 
610  // Set server hostname (sname) if non NULL value returned.
611  std::string dhcp4_server_hostname;
612  if (dhcp4_server_hostname_null_ == MLM_FALSE) {
613  dhcp4_server_hostname = std::string(dhcp4_server_hostname_,
614  dhcp4_server_hostname_length_);
615  }
616 
617  // Set boot file name (file) if non NULL value returned.
618  std::string dhcp4_boot_file_name;
619  if (dhcp4_boot_file_name_null_ == MLM_FALSE) {
620  dhcp4_boot_file_name = std::string(dhcp4_boot_file_name_,
621  dhcp4_boot_file_name_length_);
622  }
623 
624  // Set the auth key if a non empty array is retrieved
625  std::string auth_key;
626  if (auth_key_null_ == MLM_FALSE) {
627  auth_key = std::string(auth_key_, auth_key_length_);
628  }
629 
630  // Create and return Host object from the data gathered.
631  HostPtr h(new Host(dhcp_identifier_buffer_, dhcp_identifier_length_,
632  type, ipv4_subnet_id, ipv6_subnet_id, ipv4_reservation,
633  hostname, dhcp4_client_classes, dhcp6_client_classes,
634  next_server, dhcp4_server_hostname,
635  dhcp4_boot_file_name, AuthKey(auth_key)));
636  h->setHostId(host_id_);
637 
638  // Set the user context if there is one.
639  if (!user_context.empty()) {
640  try {
641  ConstElementPtr ctx = Element::fromJSON(user_context);
642  if (!ctx || (ctx->getType() != Element::map)) {
643  isc_throw(BadValue, "user context '" << user_context
644  << "' is not a JSON map");
645  }
646  h->setContext(ctx);
647  } catch (const isc::data::JSONError& ex) {
648  isc_throw(BadValue, "user context '" << user_context
649  << "' is invalid JSON: " << ex.what());
650  }
651  }
652 
653  return (h);
654  };
655 
669  virtual void processFetchedData(ConstHostCollection& hosts) {
670  HostPtr host;
671  // Add new host only if there are no hosts yet or the host id of the
672  // most recently added host is different than the host id of the
673  // currently processed host.
674  if (hosts.empty() || (hosts.back()->getHostId() != getHostId())) {
675  // Create Host object from the fetched data and append it to the
676  // collection.
677  host = retrieveHost();
678  hosts.push_back(host);
679  }
680  }
681 
692  std::string getErrorColumns() {
693  return (getColumnsInError(error_, columns_));
694  };
695 
696 protected:
697 
699  size_t columns_num_;
700 
702  std::vector<MYSQL_BIND> bind_;
703 
705  std::vector<std::string> columns_;
706 
708  std::vector<my_bools> error_;
709 
712  HostPtr host_;
713 
714 private:
715 
717  uint64_t host_id_;
718 
721  uint8_t dhcp_identifier_buffer_[DUID::MAX_DUID_LEN];
722 
724  unsigned long dhcp_identifier_length_;
725 
728  uint8_t dhcp_identifier_type_;
729 
731  uint32_t dhcp4_subnet_id_;
732 
734  uint32_t dhcp6_subnet_id_;
735 
737  uint32_t ipv4_address_;
738 
740  char hostname_[HOSTNAME_MAX_LEN];
741 
743  unsigned long hostname_length_;
744 
746  char dhcp4_client_classes_[CLIENT_CLASSES_MAX_LEN];
747 
750  unsigned long dhcp4_client_classes_length_;
751 
753  char dhcp6_client_classes_[CLIENT_CLASSES_MAX_LEN];
754 
757  unsigned long dhcp6_client_classes_length_;
758 
760  char user_context_[USER_CONTEXT_MAX_LEN];
761 
763  unsigned long user_context_length_;
764 
766  uint32_t dhcp4_next_server_;
767 
769  char dhcp4_server_hostname_[SERVER_HOSTNAME_MAX_LEN];
770 
772  unsigned long dhcp4_server_hostname_length_;
773 
775  char dhcp4_boot_file_name_[BOOT_FILE_NAME_MAX_LEN];
776 
778  unsigned long dhcp4_boot_file_name_length_;
779 
781  char auth_key_[TEXT_AUTH_KEY_LEN];
782 
784  unsigned long auth_key_length_;
785 
788 
789  my_bool dhcp4_subnet_id_null_;
791 
793  my_bool dhcp6_subnet_id_null_;
794 
796  my_bool ipv4_address_null_;
797 
799  my_bool hostname_null_;
800 
803  my_bool dhcp4_client_classes_null_;
804 
807  my_bool dhcp6_client_classes_null_;
808 
810  my_bool user_context_null_;
811 
813  my_bool dhcp4_next_server_null_;
814 
816  my_bool dhcp4_server_hostname_null_;
817 
819  my_bool dhcp4_boot_file_name_null_;
820 
822  my_bool auth_key_null_;
823 
825 };
826 
836 class MySqlHostWithOptionsExchange : public MySqlHostExchange {
837 private:
838 
840  static const size_t OPTION_COLUMNS = 7;
841 
856  class OptionProcessor {
857  public:
858 
865  OptionProcessor(const Option::Universe& universe,
866  const size_t start_column)
867  : universe_(universe), start_column_(start_column), option_id_(0),
868  code_(0), value_length_(0), formatted_value_length_(0),
869  space_length_(0), persistent_(false), user_context_length_(0),
870  option_id_null_(MLM_FALSE), code_null_(MLM_FALSE),
871  value_null_(MLM_FALSE), formatted_value_null_(MLM_FALSE),
872  space_null_(MLM_FALSE), user_context_null_(MLM_FALSE),
873  option_id_index_(start_column), code_index_(start_column_ + 1),
874  value_index_(start_column_ + 2),
875  formatted_value_index_(start_column_ + 3),
876  space_index_(start_column_ + 4),
877  persistent_index_(start_column_ + 5),
878  user_context_index_(start_column_ + 6),
879  most_recent_option_id_(0) {
880 
881  memset(value_, 0, sizeof(value_));
882  memset(formatted_value_, 0, sizeof(formatted_value_));
883  memset(space_, 0, sizeof(space_));
884  memset(user_context_, 0, sizeof(user_context_));
885  }
886 
888  uint64_t getOptionId() const {
889  if (option_id_null_ == MLM_FALSE) {
890  return (option_id_);
891  }
892  return (0);
893  }
894 
907  void retrieveOption(const CfgOptionPtr& cfg) {
908  // option_id may be NULL if dhcp4_options or dhcp6_options table
909  // doesn't contain any options for the particular host. Also, the
910  // current option id must be greater than id if the most recent
911  // option because options are ordered by option id. Otherwise
912  // we assume that this is already processed option.
913  if ((option_id_null_ == MLM_TRUE) ||
914  (most_recent_option_id_ >= option_id_)) {
915  return;
916  }
917 
918  // Remember current option id as the most recent processed one. We
919  // will be comparing it with option ids in subsequent rows.
920  most_recent_option_id_ = option_id_;
921 
922  // Convert it to string object for easier comparison.
923  std::string space;
924  if (space_null_ == MLM_FALSE) {
925  // Typically, the string values returned by the database are not
926  // NULL terminated.
927  space_[space_length_] = '\0';
928  space.assign(space_);
929  }
930 
931  // If empty or null space provided, use a default top level space.
932  if (space.empty()) {
933  space = (universe_ == Option::V4 ?
935  }
936 
937  // Convert formatted_value to string as well.
938  std::string formatted_value;
939  if (formatted_value_null_ == MLM_FALSE) {
940  formatted_value_[formatted_value_length_] = '\0';
941  formatted_value.assign(formatted_value_);
942  }
943 
944  // Convert user_context to string as well.
945  std::string user_context;
946  if (user_context_null_ == MLM_FALSE) {
947  user_context_[user_context_length_] = '\0';
948  user_context.assign(user_context_);
949  }
950 
951  // Options are held in a binary or textual format in the database.
952  // This is similar to having an option specified in a server
953  // configuration file. Such option is converted to appropriate C++
954  // class, using option definition. Thus, we need to find the
955  // option definition for this option code and option space.
956 
957  // Check if this is a standard option.
958  OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code_);
959 
960  // Otherwise, we may check if this an option encapsulated within the
961  // vendor space.
962  if (!def && (space != DHCP4_OPTION_SPACE) &&
963  (space != DHCP6_OPTION_SPACE)) {
964  uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
965  if (vendor_id > 0) {
966  def = LibDHCP::getVendorOptionDef(universe_, vendor_id, code_);
967  }
968  }
969 
970  // In all other cases, we use runtime option definitions, which
971  // should be also registered within the libdhcp++.
972  if (!def) {
973  def = LibDHCP::getRuntimeOptionDef(space, code_);
974  }
975 
976  OptionPtr option;
977 
978  if (!def) {
979  // If no definition found, we use generic option type.
980  OptionBuffer buf(value_, value_ + value_length_);
981  option.reset(new Option(universe_, code_, buf.begin(),
982  buf.end()));
983  } else {
984  // The option value may be specified in textual or binary format
985  // in the database. If formatted_value is empty, the binary
986  // format is used. Depending on the format we use a different
987  // variant of the optionFactory function.
988  if (formatted_value.empty()) {
989  OptionBuffer buf(value_, value_ + value_length_);
990  option = def->optionFactory(universe_, code_, buf.begin(),
991  buf.end());
992  } else {
993  // Spit the value specified in comma separated values
994  // format.
995  std::vector<std::string> split_vec;
996  boost::split(split_vec, formatted_value, boost::is_any_of(","));
997  option = def->optionFactory(universe_, code_, split_vec);
998  }
999  }
1000 
1001  OptionDescriptor desc(option, persistent_, formatted_value);
1002 
1003  // Set the user context if there is one into the option descriptor.
1004  if (!user_context.empty()) {
1005  try {
1006  ConstElementPtr ctx = Element::fromJSON(user_context);
1007  if (!ctx || (ctx->getType() != Element::map)) {
1008  isc_throw(BadValue, "user context '" << user_context
1009  << "' is no a JSON map");
1010  }
1011  desc.setContext(ctx);
1012  } catch (const isc::data::JSONError& ex) {
1013  isc_throw(BadValue, "user context '" << user_context
1014  << "' is invalid JSON: " << ex.what());
1015  }
1016  }
1017 
1018  cfg->add(desc, space);
1019  }
1020 
1025  void setColumnNames(std::vector<std::string>& columns) {
1026  columns[option_id_index_] = "option_id";
1027  columns[code_index_] = "code";
1028  columns[value_index_] = "value";
1029  columns[formatted_value_index_] = "formatted_value";
1030  columns[space_index_] = "space";
1031  columns[persistent_index_] = "persistent";
1032  columns[user_context_index_] = "user_context";
1033  }
1034 
1041  void setBindFields(std::vector<MYSQL_BIND>& bind) {
1042  // This method is called just before making a new query, so we
1043  // reset the most_recent_option_id_ and other exchange members to
1044  // start over with options processing.
1045  most_recent_option_id_ = 0;
1046 
1047  option_id_ = 0;
1048  code_ = 0;
1049  persistent_ = false;
1050  option_id_null_ = MLM_FALSE;
1051  code_null_ = MLM_FALSE;
1052  value_null_ = MLM_FALSE;
1053  formatted_value_null_ = MLM_FALSE;
1054  space_null_ = MLM_FALSE;
1055  user_context_null_ = MLM_FALSE;
1056 
1057  memset(value_, 0, sizeof(value_));
1058  memset(formatted_value_, 0, sizeof(formatted_value_));
1059  memset(space_, 0, sizeof(space_));
1060  memset(user_context_, 0, sizeof(user_context_));
1061 
1062  // option_id : INT UNSIGNED NOT NULL AUTO_INCREMENT,
1063  bind[option_id_index_].buffer_type = MYSQL_TYPE_LONG;
1064  bind[option_id_index_].buffer = reinterpret_cast<char*>(&option_id_);
1065  bind[option_id_index_].is_unsigned = MLM_TRUE;
1066  bind[option_id_index_].is_null = &option_id_null_;
1067 
1068  // code : TINYINT OR SHORT UNSIGNED NOT NULL
1069  bind[code_index_].buffer_type = MYSQL_TYPE_SHORT;
1070  bind[code_index_].buffer = reinterpret_cast<char*>(&code_);
1071  bind[code_index_].is_unsigned = MLM_TRUE;
1072  bind[code_index_].is_null = &code_null_;
1073 
1074  // value : BLOB NULL
1075  value_length_ = sizeof(value_);
1076  bind[value_index_].buffer_type = MYSQL_TYPE_BLOB;
1077  bind[value_index_].buffer = reinterpret_cast<char*>(value_);
1078  bind[value_index_].buffer_length = value_length_;
1079  bind[value_index_].length = &value_length_;
1080  bind[value_index_].is_null = &value_null_;
1081 
1082  // formatted_value : TEXT NULL
1083  formatted_value_length_ = sizeof(formatted_value_);
1084  bind[formatted_value_index_].buffer_type = MYSQL_TYPE_STRING;
1085  bind[formatted_value_index_].buffer = reinterpret_cast<char*>(formatted_value_);
1086  bind[formatted_value_index_].buffer_length = formatted_value_length_;
1087  bind[formatted_value_index_].length = &formatted_value_length_;
1088  bind[formatted_value_index_].is_null = &formatted_value_null_;
1089 
1090  // space : VARCHAR(128) NULL
1091  space_length_ = sizeof(space_);
1092  bind[space_index_].buffer_type = MYSQL_TYPE_STRING;
1093  bind[space_index_].buffer = reinterpret_cast<char*>(space_);
1094  bind[space_index_].buffer_length = space_length_;
1095  bind[space_index_].length = &space_length_;
1096  bind[space_index_].is_null = &space_null_;
1097 
1098  // persistent : TINYINT(1) NOT NULL DEFAULT 0
1099  bind[persistent_index_].buffer_type = MYSQL_TYPE_TINY;
1100  bind[persistent_index_].buffer = reinterpret_cast<char*>(&persistent_);
1101  bind[persistent_index_].is_unsigned = MLM_TRUE;
1102 
1103  // user_context : TEXT NULL
1104  user_context_length_ = sizeof(user_context_);
1105  bind[user_context_index_].buffer_type = MYSQL_TYPE_STRING;
1106  bind[user_context_index_].buffer = reinterpret_cast<char*>(user_context_);
1107  bind[user_context_index_].buffer_length = user_context_length_;
1108  bind[user_context_index_].length = &user_context_length_;
1109  bind[user_context_index_].is_null = &user_context_null_;
1110  }
1111 
1112  private:
1113 
1115  Option::Universe universe_;
1116 
1118  size_t start_column_;
1119 
1121  uint32_t option_id_;
1122 
1124  uint16_t code_;
1125 
1127  uint8_t value_[OPTION_VALUE_MAX_LEN];
1128 
1130  unsigned long value_length_;
1131 
1133  char formatted_value_[OPTION_FORMATTED_VALUE_MAX_LEN];
1134 
1136  unsigned long formatted_value_length_;
1137 
1139  char space_[OPTION_SPACE_MAX_LEN];
1140 
1142  unsigned long space_length_;
1143 
1146  bool persistent_;
1147 
1149  char user_context_[USER_CONTEXT_MAX_LEN];
1150 
1152  unsigned long user_context_length_;
1153 
1156 
1157  my_bool option_id_null_;
1159 
1161  my_bool code_null_;
1162 
1164  my_bool value_null_;
1165 
1168  my_bool formatted_value_null_;
1169 
1171  my_bool space_null_;
1172 
1174  my_bool user_context_null_;
1176 
1178 
1179  size_t option_id_index_;
1181 
1183  size_t code_index_;
1184 
1186  size_t value_index_;
1187 
1189  size_t formatted_value_index_;
1190 
1192  size_t space_index_;
1193 
1195  size_t persistent_index_;
1197 
1199  size_t user_context_index_;
1200 
1202  uint32_t most_recent_option_id_;
1203  };
1204 
1206  typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
1207 
1208 public:
1209 
1216  enum FetchedOptions {
1217  DHCP4_ONLY,
1218  DHCP6_ONLY,
1219  DHCP4_AND_DHCP6
1220  };
1221 
1230  MySqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
1231  const size_t additional_columns_num = 0)
1232  : MySqlHostExchange(getRequiredColumnsNum(fetched_options)
1233  + additional_columns_num),
1234  opt_proc4_(), opt_proc6_() {
1235 
1236  // Create option processor for DHCPv4 options, if required.
1237  if ((fetched_options == DHCP4_ONLY) ||
1238  (fetched_options == DHCP4_AND_DHCP6)) {
1239  opt_proc4_.reset(new OptionProcessor(Option::V4,
1240  findAvailColumn()));
1241  opt_proc4_->setColumnNames(columns_);
1242  }
1243 
1244  // Create option processor for DHCPv6 options, if required.
1245  if ((fetched_options == DHCP6_ONLY) ||
1246  (fetched_options == DHCP4_AND_DHCP6)) {
1247  opt_proc6_.reset(new OptionProcessor(Option::V6,
1248  findAvailColumn()));
1249  opt_proc6_->setColumnNames(columns_);
1250  }
1251  }
1252 
1261  virtual void processFetchedData(ConstHostCollection& hosts) {
1262  // Holds pointer to the previously parsed host.
1263  HostPtr most_recent_host;
1264  if (!hosts.empty()) {
1265  // Const cast is not very elegant way to deal with it, but
1266  // there is a good reason to use it here. This method is called
1267  // to build a collection of const hosts to be returned to the
1268  // caller. If we wanted to use non-const collection we'd need
1269  // to copy the whole collection before returning it, which has
1270  // performance implications. Alternatively, we could store the
1271  // most recently added host in a class member but this would
1272  // make the code less readable.
1273  most_recent_host = boost::const_pointer_cast<Host>(hosts.back());
1274  }
1275 
1276  // If no host has been parsed yet or we're at the row holding next
1277  // host, we create a new host object and put it at the end of the
1278  // list.
1279  if (!most_recent_host || (most_recent_host->getHostId() < getHostId())) {
1280  HostPtr host = retrieveHost();
1281  hosts.push_back(host);
1282  most_recent_host = host;
1283  }
1284 
1285  // Parse DHCPv4 options if required to do so.
1286  if (opt_proc4_) {
1287  CfgOptionPtr cfg = most_recent_host->getCfgOption4();
1288  opt_proc4_->retrieveOption(cfg);
1289  }
1290 
1291  // Parse DHCPv6 options if required to do so.
1292  if (opt_proc6_) {
1293  CfgOptionPtr cfg = most_recent_host->getCfgOption6();
1294  opt_proc6_->retrieveOption(cfg);
1295  }
1296  }
1297 
1301  virtual std::vector<MYSQL_BIND> createBindForReceive() {
1302  // The following call sets bind_ values between 0 and 8.
1303  static_cast<void>(MySqlHostExchange::createBindForReceive());
1304 
1305  // Bind variables for DHCPv4 options.
1306  if (opt_proc4_) {
1307  opt_proc4_->setBindFields(bind_);
1308  }
1309 
1310  // Bind variables for DHCPv6 options.
1311  if (opt_proc6_) {
1312  opt_proc6_->setBindFields(bind_);
1313  }
1314 
1315  // Add the error flags
1316  setErrorIndicators(bind_, error_);
1317 
1318  return (bind_);
1319  };
1320 
1321 private:
1322 
1334  static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
1335  return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
1336  OPTION_COLUMNS);
1337  }
1338 
1342  OptionProcessorPtr opt_proc4_;
1343 
1347  OptionProcessorPtr opt_proc6_;
1348 };
1349 
1362 class MySqlHostIPv6Exchange : public MySqlHostWithOptionsExchange {
1363 private:
1364 
1366  static const size_t RESERVATION_COLUMNS = 5;
1367 
1368 public:
1369 
1374  MySqlHostIPv6Exchange(const FetchedOptions& fetched_options)
1375  : MySqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
1376  reservation_id_(0),
1377  reserv_type_(0), reserv_type_null_(MLM_FALSE),
1378  ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0),
1379  reservation_id_index_(findAvailColumn()),
1380  address_index_(reservation_id_index_ + 1),
1381  prefix_len_index_(reservation_id_index_ + 2),
1382  type_index_(reservation_id_index_ + 3),
1383  iaid_index_(reservation_id_index_ + 4),
1384  most_recent_reservation_id_(0) {
1385 
1386  memset(ipv6_address_buffer_, 0, sizeof(ipv6_address_buffer_));
1387 
1388  // Provide names of additional columns returned by the queries.
1389  columns_[reservation_id_index_] = "reservation_id";
1390  columns_[address_index_] = "address";
1391  columns_[prefix_len_index_] = "prefix_len";
1392  columns_[type_index_] = "type";
1393  columns_[iaid_index_] = "dhcp6_iaid";
1394  }
1395 
1399  uint32_t getReservationId() const {
1400  if (reserv_type_null_ == MLM_FALSE) {
1401  return (reservation_id_);
1402  }
1403  return (0);
1404  };
1405 
1412  IPv6Resrv retrieveReservation() {
1413  // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
1414  IPv6Resrv::Type type = IPv6Resrv::TYPE_NA;
1415 
1416  switch (reserv_type_) {
1417  case 0:
1418  type = IPv6Resrv::TYPE_NA;
1419  break;
1420 
1421  case 2:
1422  type = IPv6Resrv::TYPE_PD;
1423  break;
1424 
1425  default:
1427  "invalid IPv6 reservation type returned: "
1428  << static_cast<int>(reserv_type_)
1429  << ". Only 0 or 2 are allowed.");
1430  }
1431 
1432  ipv6_address_buffer_[ipv6_address_buffer_len_] = '\0';
1433  std::string address = ipv6_address_buffer_;
1434  IPv6Resrv r(type, IOAddress(address), prefix_len_);
1435  return (r);
1436  };
1437 
1457  virtual void processFetchedData(ConstHostCollection& hosts) {
1458 
1459  // Call parent class to fetch host information and options.
1460  MySqlHostWithOptionsExchange::processFetchedData(hosts);
1461 
1462  if (getReservationId() == 0) {
1463  return;
1464  }
1465 
1466  if (hosts.empty()) {
1467  isc_throw(Unexpected, "no host information while retrieving"
1468  " IPv6 reservation");
1469  }
1470  HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
1471 
1472  // If we're dealing with a new reservation, let's add it to the
1473  // host.
1474  if (getReservationId() > most_recent_reservation_id_) {
1475  most_recent_reservation_id_ = getReservationId();
1476 
1477  if (most_recent_reservation_id_ > 0) {
1478  host->addReservation(retrieveReservation());
1479  }
1480  }
1481  }
1482 
1491  virtual std::vector<MYSQL_BIND> createBindForReceive() {
1492  // Reset most recent reservation id value because we're now making
1493  // a new SELECT query.
1494  most_recent_reservation_id_ = 0;
1495 
1496  // Bind values supported by parent classes.
1497  static_cast<void>(MySqlHostWithOptionsExchange::createBindForReceive());
1498 
1499  // reservation_id : INT UNSIGNED NOT NULL AUTO_INCREMENT
1500  bind_[reservation_id_index_].buffer_type = MYSQL_TYPE_LONG;
1501  bind_[reservation_id_index_].buffer = reinterpret_cast<char*>(&reservation_id_);
1502  bind_[reservation_id_index_].is_unsigned = MLM_TRUE;
1503 
1504  // IPv6 address/prefix VARCHAR(39)
1505  ipv6_address_buffer_len_ = sizeof(ipv6_address_buffer_) - 1;
1506  bind_[address_index_].buffer_type = MYSQL_TYPE_STRING;
1507  bind_[address_index_].buffer = ipv6_address_buffer_;
1508  bind_[address_index_].buffer_length = ipv6_address_buffer_len_;
1509  bind_[address_index_].length = &ipv6_address_buffer_len_;
1510 
1511  // prefix_len : TINYINT
1512  bind_[prefix_len_index_].buffer_type = MYSQL_TYPE_TINY;
1513  bind_[prefix_len_index_].buffer = reinterpret_cast<char*>(&prefix_len_);
1514  bind_[prefix_len_index_].is_unsigned = MLM_TRUE;
1515 
1516  // (reservation) type : TINYINT
1517  reserv_type_null_ = MLM_FALSE;
1518  bind_[type_index_].buffer_type = MYSQL_TYPE_TINY;
1519  bind_[type_index_].buffer = reinterpret_cast<char*>(&reserv_type_);
1520  bind_[type_index_].is_unsigned = MLM_TRUE;
1521  bind_[type_index_].is_null = &reserv_type_null_;
1522 
1523  // dhcp6_iaid INT UNSIGNED
1524  bind_[iaid_index_].buffer_type = MYSQL_TYPE_LONG;
1525  bind_[iaid_index_].buffer = reinterpret_cast<char*>(&iaid_);
1526  bind_[iaid_index_].is_unsigned = MLM_TRUE;
1527 
1528  // Add the error flags
1529  setErrorIndicators(bind_, error_);
1530 
1531  return (bind_);
1532  };
1533 
1534 private:
1535 
1537  uint32_t reservation_id_;
1538 
1540  uint8_t reserv_type_;
1541 
1546  my_bool reserv_type_null_;
1547 
1549  char ipv6_address_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];
1550 
1552  unsigned long ipv6_address_buffer_len_;
1553 
1555  uint8_t prefix_len_;
1556 
1558  uint32_t iaid_;
1559 
1561 
1562  size_t reservation_id_index_;
1564 
1566  size_t address_index_;
1567 
1569  size_t prefix_len_index_;
1570 
1572  size_t type_index_;
1573 
1575  size_t iaid_index_;
1576 
1578 
1580  uint32_t most_recent_reservation_id_;
1581 };
1582 
1593 class MySqlIPv6ReservationExchange {
1594 private:
1595 
1597  static const size_t RESRV_COLUMNS = 6;
1598 
1599 public:
1600 
1604  MySqlIPv6ReservationExchange()
1605  : host_id_(0), address_("::"), address_len_(0), prefix_len_(0), type_(0),
1606  iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
1607 
1608  // Reset error table.
1609  std::fill(&error_[0], &error_[RESRV_COLUMNS], MLM_FALSE);
1610 
1611  // Set the column names (for error messages)
1612  columns_[0] = "host_id";
1613  columns_[1] = "address";
1614  columns_[2] = "prefix_len";
1615  columns_[3] = "type";
1616  columns_[4] = "dhcp6_iaid";
1617  BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
1618  }
1619 
1634  std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv,
1635  const HostID& id,
1636  const bool unique_ip) {
1637 
1638  // Store the values to ensure they remain valid.
1639  resv_ = resv;
1640  host_id_ = id;
1641 
1642  // Initialize prior to constructing the array of MYSQL_BIND structures.
1643  // It sets all fields, including is_null, to zero, so we need to set
1644  // is_null only if it should be true. This gives up minor performance
1645  // benefit while being safe approach. For improved readability, the
1646  // code that explicitly sets is_null is there, but is commented out.
1647  memset(bind_, 0, sizeof(bind_));
1648 
1649  // Set up the structures for the various components of the host structure.
1650 
1651  try {
1652  // address VARCHAR(39)
1653  address_ = resv.getPrefix().toText();
1654  address_len_ = address_.length();
1655  bind_[0].buffer_type = MYSQL_TYPE_BLOB;
1656  bind_[0].buffer = reinterpret_cast<char*>
1657  (const_cast<char*>(address_.c_str()));
1658  bind_[0].buffer_length = address_len_;
1659  bind_[0].length = &address_len_;
1660 
1661  // prefix_len tinyint
1662  prefix_len_ = resv.getPrefixLen();
1663  bind_[1].buffer_type = MYSQL_TYPE_TINY;
1664  bind_[1].buffer = reinterpret_cast<char*>(&prefix_len_);
1665  bind_[1].is_unsigned = MLM_TRUE;
1666 
1667  // type tinyint
1668  // See lease6_types for values (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
1669  type_ = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1670  bind_[2].buffer_type = MYSQL_TYPE_TINY;
1671  bind_[2].buffer = reinterpret_cast<char*>(&type_);
1672  bind_[2].is_unsigned = MLM_TRUE;
1673 
1674  // dhcp6_iaid INT UNSIGNED
1676  iaid_ = 0;
1677  bind_[3].buffer_type = MYSQL_TYPE_LONG;
1678  bind_[3].buffer = reinterpret_cast<char*>(&iaid_);
1679  bind_[3].is_unsigned = MLM_TRUE;
1680 
1681  // host_id INT UNSIGNED NOT NULL
1682  bind_[4].buffer_type = MYSQL_TYPE_LONG;
1683  bind_[4].buffer = reinterpret_cast<char*>(&host_id_);
1684  bind_[4].is_unsigned = MLM_TRUE;
1685 
1686  } catch (const std::exception& ex) {
1688  "Could not create bind array from IPv6 Reservation: "
1689  << resv_.toText() << ", reason: " << ex.what());
1690  }
1691 
1692  // Add the data to the vector. Note the end element is one after the
1693  // end of the array.
1694  // RESRV_COLUMNS -1 as we do not set reservation_id.
1695  std::vector<MYSQL_BIND> vec(&bind_[0], &bind_[RESRV_COLUMNS-1]);
1696 
1697  // When checking whether the IP is unique we need to bind the IPv6 address
1698  // and prefix length at the end of the query as it has additional binding
1699  // for the IPv6 address and prefix length.
1700  if (unique_ip) {
1701  vec.push_back(bind_[0]); // address
1702  vec.push_back(bind_[1]); // prefix_len
1703  }
1704 
1705  return (vec);
1706  }
1707 
1708 private:
1709 
1711  uint64_t host_id_;
1712 
1714  std::string address_;
1715 
1717  unsigned long address_len_;
1718 
1720  uint8_t prefix_len_;
1721 
1723  uint8_t type_;
1724 
1726  uint8_t iaid_;
1727 
1729  IPv6Resrv resv_;
1730 
1732  MYSQL_BIND bind_[RESRV_COLUMNS];
1733 
1735  std::string columns_[RESRV_COLUMNS];
1736 
1739  my_bool error_[RESRV_COLUMNS];
1740 };
1741 
1745 class MySqlOptionExchange {
1746 private:
1747 
1749  static const size_t OPTION_COLUMNS = 10;
1750 
1751 public:
1752 
1754  MySqlOptionExchange()
1755  : type_(0), value_len_(0), formatted_value_len_(0), space_(),
1756  space_len_(0), persistent_(false), user_context_(),
1757  user_context_len_(0), subnet_id_(SUBNET_ID_UNUSED),
1758  host_id_(0), option_() {
1759 
1760  BOOST_STATIC_ASSERT(9 < OPTION_COLUMNS);
1761  }
1762 
1766  std::vector<MYSQL_BIND> createBindForSend(const OptionDescriptor& opt_desc,
1767  const std::string& opt_space,
1768  const Optional<SubnetID>& subnet_id,
1769  const HostID& host_id) {
1770 
1771  // Hold pointer to the option to make sure it remains valid until
1772  // we complete a query.
1773  option_ = opt_desc.option_;
1774 
1775  memset(bind_, 0, sizeof(bind_));
1776 
1777  try {
1778  // option_id: INT UNSIGNED NOT NULL
1779  // The option_id is auto_incremented, so we need to pass the NULL
1780  // value.
1781  bind_[0].buffer_type = MYSQL_TYPE_NULL;
1782 
1783  // code: SMALLINT UNSIGNED NOT NULL
1784  type_ = option_->getType();
1785  bind_[1].buffer_type = MYSQL_TYPE_SHORT;
1786  bind_[1].buffer = reinterpret_cast<char*>(&type_);
1787  bind_[1].is_unsigned = MLM_TRUE;
1788 
1789  // value: BLOB NULL
1790  if (opt_desc.formatted_value_.empty() &&
1791  (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
1792  // The formatted_value is empty and the option value is
1793  // non-empty so we need to prepare on-wire format for the
1794  // option and store it in the database as a blob.
1795  OutputBuffer buf(opt_desc.option_->len());
1796  opt_desc.option_->pack(buf);
1797  const char* buf_ptr = static_cast<const char*>(buf.getData());
1798  value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
1799  buf_ptr + buf.getLength());
1800  value_len_ = value_.size();
1801  bind_[2].buffer_type = MYSQL_TYPE_BLOB;
1802  bind_[2].buffer = &value_[0];
1803  bind_[2].buffer_length = value_len_;
1804  bind_[2].length = &value_len_;
1805 
1806  } else {
1807  // No value or formatted_value specified. In this case, the
1808  // value blob is NULL.
1809  value_.clear();
1810  bind_[2].buffer_type = MYSQL_TYPE_NULL;
1811  }
1812 
1813  // formatted_value: TEXT NULL,
1814  if (!opt_desc.formatted_value_.empty()) {
1815  formatted_value_len_ = opt_desc.formatted_value_.size();
1816  bind_[3].buffer_type = MYSQL_TYPE_STRING;
1817  bind_[3].buffer = const_cast<char*>(opt_desc.formatted_value_.c_str());
1818  bind_[3].buffer_length = formatted_value_len_;
1819  bind_[3].length = &formatted_value_len_;
1820 
1821  } else {
1822  bind_[3].buffer_type = MYSQL_TYPE_NULL;
1823  }
1824 
1825  // space: VARCHAR(128) NULL
1826  space_ = opt_space;
1827  space_len_ = space_.size();
1828  bind_[4].buffer_type = MYSQL_TYPE_STRING;
1829  bind_[4].buffer = const_cast<char*>(space_.c_str());
1830  bind_[4].buffer_length = space_len_;
1831  bind_[4].length = &space_len_;
1832 
1833  // persistent: TINYINT(1) NOT NULL DEFAULT 0
1834  persistent_ = opt_desc.persistent_;
1835  bind_[5].buffer_type = MYSQL_TYPE_TINY;
1836  bind_[5].buffer = reinterpret_cast<char*>(&persistent_);
1837  bind_[5].is_unsigned = MLM_TRUE;
1838 
1839  // user_context: TEST NULL,
1840  ConstElementPtr ctx = opt_desc.getContext();
1841  if (ctx) {
1842  user_context_ = ctx->str();
1843  user_context_len_ = user_context_.size();
1844  bind_[6].buffer_type = MYSQL_TYPE_STRING;
1845  bind_[6].buffer = const_cast<char*>(user_context_.c_str());
1846  bind_[6].buffer_length = user_context_len_;
1847  bind_[6].length = &user_context_len_;
1848  } else {
1849  bind_[6].buffer_type = MYSQL_TYPE_NULL;
1850  }
1851 
1852  // dhcp4_subnet_id: INT UNSIGNED NULL
1853  if (!subnet_id.unspecified()) {
1854  subnet_id_ = subnet_id;
1855  bind_[7].buffer_type = MYSQL_TYPE_LONG;
1856  bind_[7].buffer = reinterpret_cast<char*>(subnet_id_);
1857  bind_[7].is_unsigned = MLM_TRUE;
1858 
1859  } else {
1860  bind_[7].buffer_type = MYSQL_TYPE_NULL;
1861  }
1862 
1863  // host_id: INT UNSIGNED NOT NULL
1864  host_id_ = host_id;
1865  bind_[8].buffer_type = MYSQL_TYPE_LONG;
1866  bind_[8].buffer = reinterpret_cast<char*>(&host_id_);
1867  bind_[8].is_unsigned = MLM_TRUE;
1868 
1869  } catch (const std::exception& ex) {
1871  "Could not create bind array for inserting DHCP "
1872  "option: " << option_->toText() << ", reason: "
1873  << ex.what());
1874  }
1875 
1876  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[OPTION_COLUMNS]));
1877  }
1878 
1879 private:
1880 
1882  uint16_t type_;
1883 
1885  std::vector<uint8_t> value_;
1886 
1888  unsigned long value_len_;
1889 
1891  unsigned long formatted_value_len_;
1892 
1894  std::string space_;
1895 
1897  unsigned long space_len_;
1898 
1901  bool persistent_;
1902 
1904  std::string user_context_;
1905 
1907  unsigned long user_context_len_;
1908 
1910  uint32_t subnet_id_;
1911 
1913  uint32_t host_id_;
1914 
1916  OptionPtr option_;
1917 
1919  MYSQL_BIND bind_[OPTION_COLUMNS];
1920 };
1921 
1922 } // namespace
1923 
1924 namespace isc {
1925 namespace dhcp {
1926 
1937 public:
1938 
1945  IOServiceAccessorPtr io_service_accessor,
1946  db::DbCallback db_reconnect_callback);
1947 
1952 
1955  boost::shared_ptr<MySqlHostWithOptionsExchange> host_ipv4_exchange_;
1956 
1959  boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv6_exchange_;
1960 
1964  boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv46_exchange_;
1965 
1968  boost::shared_ptr<MySqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
1969 
1973  boost::shared_ptr<MySqlOptionExchange> host_option_exchange_;
1974 
1977 
1980 };
1981 
1989 public:
1990 
1992  std::vector<MySqlHostContextPtr> pool_;
1993 
1995  std::mutex mutex_;
1996 };
1997 
1999 typedef boost::shared_ptr<MySqlHostContextPool> MySqlHostContextPoolPtr;
2000 
2003 public:
2004 
2014  GET_HOST_DHCPID, // Gets hosts by host identifier
2015  GET_HOST_ADDR, // Gets hosts by IPv4 address
2016  GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
2017  GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
2018  GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
2019  GET_HOST_PREFIX, // Gets host by IPv6 prefix
2020  GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
2021  GET_HOST_SUBID4, // Gets hosts by IPv4 SubnetID
2022  GET_HOST_SUBID6, // Gets hosts by IPv6 SubnetID
2023  GET_HOST_HOSTNAME, // Gets hosts by hostname
2024  GET_HOST_HOSTNAME_SUBID4, // Gets hosts by hostname and IPv4 SubnetID
2025  GET_HOST_HOSTNAME_SUBID6, // Gets hosts by hostname and IPv6 SubnetID
2026  GET_HOST_SUBID4_PAGE, // Gets hosts by IPv4 SubnetID beginning by HID
2027  GET_HOST_SUBID6_PAGE, // Gets hosts by IPv6 SubnetID beginning by HID
2028  GET_HOST_PAGE4, // Gets v4 hosts beginning by HID
2029  GET_HOST_PAGE6, // Gets v6 hosts beginning by HID
2030  INSERT_HOST_NON_UNIQUE_IP, // Insert new host to collection with allowing IP duplicates
2031  INSERT_HOST_UNIQUE_IP, // Insert new host to collection with checking for IP duplicates
2032  INSERT_V6_RESRV_NON_UNIQUE,// Insert v6 reservation without checking that it is unique
2033  INSERT_V6_RESRV_UNIQUE, // Insert v6 reservation with checking that it is unique
2034  INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
2035  INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
2036  DEL_HOST_ADDR4, // Delete v4 host (subnet-id, addr4)
2037  DEL_HOST_ADDR6, // Delete v6 host (subnet-id, addr6)
2038  DEL_HOST_SUBID4_ID, // Delete v4 host (subnet-id, ident.type, identifier)
2039  DEL_HOST_SUBID6_ID, // Delete v6 host (subnet-id, ident.type, identifier)
2040  NUM_STATEMENTS // Number of statements
2041  };
2042 
2048  static const StatementIndex WRITE_STMTS_BEGIN = INSERT_HOST_NON_UNIQUE_IP;
2049 
2055 
2058 
2081  static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl);
2082 
2092  MySqlHostContextPtr createContext() const;
2093 
2105  std::pair<uint32_t, uint32_t> getVersion() const;
2106 
2115  void addStatement(MySqlHostContextPtr& ctx,
2117  std::vector<MYSQL_BIND>& bind);
2118 
2127  bool delStatement(MySqlHostContextPtr& ctx,
2128  StatementIndex stindex,
2129  MYSQL_BIND* bind);
2130 
2136  void addResv(MySqlHostContextPtr& ctx,
2137  const IPv6Resrv& resv,
2138  const HostID& id);
2139 
2149  void addOption(MySqlHostContextPtr& ctx,
2151  const OptionDescriptor& opt_desc,
2152  const std::string& opt_space,
2153  const Optional<SubnetID>& subnet_id,
2154  const HostID& host_id);
2155 
2163  void addOptions(MySqlHostContextPtr& ctx,
2164  const StatementIndex& stindex,
2165  const ConstCfgOptionPtr& options_cfg,
2166  const uint64_t host_id);
2167 
2179  void checkError(MySqlHostContextPtr& ctx,
2180  const int status,
2181  const StatementIndex index,
2182  const char* what) const;
2183 
2202  void getHostCollection(MySqlHostContextPtr& ctx,
2203  StatementIndex stindex,
2204  MYSQL_BIND* bind,
2205  boost::shared_ptr<MySqlHostExchange> exchange,
2206  ConstHostCollection& result,
2207  bool single) const;
2208 
2226  ConstHostPtr getHost(MySqlHostContextPtr& ctx,
2227  const SubnetID& subnet_id,
2228  const Host::IdentifierType& identifier_type,
2229  const uint8_t* identifier_begin,
2230  const size_t identifier_len,
2231  StatementIndex stindex,
2232  boost::shared_ptr<MySqlHostExchange> exchange) const;
2233 
2243  void checkReadOnly(MySqlHostContextPtr& ctx) const;
2244 
2247 
2251 
2253  MySqlHostContextPoolPtr pool_;
2254 
2258 
2260  std::string timer_name_;
2261 };
2262 
2263 namespace {
2264 
2266 typedef boost::array<TaggedStatement, MySqlHostDataSourceImpl::NUM_STATEMENTS>
2267 TaggedStatementArray;
2268 
2271 TaggedStatementArray tagged_statements = { {
2272  // Retrieves host information, IPv6 reservations and both DHCPv4 and
2273  // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2274  // to retrieve information from 4 different tables using a single query.
2275  // Hence, this query returns multiple rows for a single host.
2276  {MySqlHostDataSourceImpl::GET_HOST_DHCPID,
2277  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2278  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2279  "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2280  "h.user_context, "
2281  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2282  "h.dhcp4_boot_file_name, h.auth_key, "
2283  "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2284  "o4.persistent, o4.user_context, "
2285  "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2286  "o6.persistent, o6.user_context, "
2287  "r.reservation_id, r.address, r.prefix_len, r.type, "
2288  "r.dhcp6_iaid "
2289  "FROM hosts AS h "
2290  "LEFT JOIN dhcp4_options AS o4 "
2291  "ON h.host_id = o4.host_id "
2292  "LEFT JOIN dhcp6_options AS o6 "
2293  "ON h.host_id = o6.host_id "
2294  "LEFT JOIN ipv6_reservations AS r "
2295  "ON h.host_id = r.host_id "
2296  "WHERE dhcp_identifier = ? AND dhcp_identifier_type = ? "
2297  "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2298 
2299  // Retrieves host information along with the DHCPv4 options associated with
2300  // it. Left joining the dhcp4_options table results in multiple rows being
2301  // returned for the same host. The host is retrieved by IPv4 address.
2302  {MySqlHostDataSourceImpl::GET_HOST_ADDR,
2303  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2304  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2305  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2306  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2307  "h.dhcp4_boot_file_name, h.auth_key, "
2308  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2309  "o.persistent, o.user_context "
2310  "FROM hosts AS h "
2311  "LEFT JOIN dhcp4_options AS o "
2312  "ON h.host_id = o.host_id "
2313  "WHERE ipv4_address = ? "
2314  "ORDER BY h.host_id, o.option_id"},
2315 
2316  // Retrieves host information and DHCPv4 options using subnet identifier
2317  // and client's identifier. Left joining the dhcp4_options table results in
2318  // multiple rows being returned for the same host.
2319  {MySqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID,
2320  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2321  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2322  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2323  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2324  "h.dhcp4_boot_file_name, h.auth_key, "
2325  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2326  "o.persistent, o.user_context "
2327  "FROM hosts AS h "
2328  "LEFT JOIN dhcp4_options AS o "
2329  "ON h.host_id = o.host_id "
2330  "WHERE h.dhcp4_subnet_id = ? AND h.dhcp_identifier_type = ? "
2331  "AND h.dhcp_identifier = ? "
2332  "ORDER BY h.host_id, o.option_id"},
2333 
2334  // Retrieves host information, IPv6 reservations and DHCPv6 options
2335  // associated with a host. The number of rows returned is a multiplication
2336  // of number of IPv6 reservations and DHCPv6 options.
2337  {MySqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID,
2338  "SELECT h.host_id, h.dhcp_identifier, "
2339  "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2340  "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2341  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2342  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2343  "h.dhcp4_boot_file_name, h.auth_key, "
2344  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2345  "o.persistent, o.user_context, "
2346  "r.reservation_id, r.address, r.prefix_len, r.type, "
2347  "r.dhcp6_iaid "
2348  "FROM hosts AS h "
2349  "LEFT JOIN dhcp6_options AS o "
2350  "ON h.host_id = o.host_id "
2351  "LEFT JOIN ipv6_reservations AS r "
2352  "ON h.host_id = r.host_id "
2353  "WHERE h.dhcp6_subnet_id = ? AND h.dhcp_identifier_type = ? "
2354  "AND h.dhcp_identifier = ? "
2355  "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2356 
2357  // Retrieves host information and DHCPv4 options for the host using subnet
2358  // identifier and IPv4 reservation. Left joining the dhcp4_options table
2359  // results in multiple rows being returned for the host. The number of
2360  // rows depends on the number of options defined for the host.
2361  {MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
2362  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2363  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2364  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2365  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2366  "h.dhcp4_boot_file_name, h.auth_key, "
2367  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2368  "o.persistent, o.user_context "
2369  "FROM hosts AS h "
2370  "LEFT JOIN dhcp4_options AS o "
2371  "ON h.host_id = o.host_id "
2372  "WHERE h.dhcp4_subnet_id = ? AND h.ipv4_address = ? "
2373  "ORDER BY h.host_id, o.option_id"},
2374 
2375  // Retrieves host information, IPv6 reservations and DHCPv6 options
2376  // associated with a host using prefix and prefix length. This query
2377  // returns host information for a single host. However, multiple rows
2378  // are returned due to left joining IPv6 reservations and DHCPv6 options.
2379  // The number of rows returned is multiplication of number of existing
2380  // IPv6 reservations and DHCPv6 options.
2381  {MySqlHostDataSourceImpl::GET_HOST_PREFIX,
2382  "SELECT h.host_id, h.dhcp_identifier, "
2383  "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2384  "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2385  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2386  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2387  "h.dhcp4_boot_file_name, h.auth_key, "
2388  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2389  "o.persistent, o.user_context,"
2390  "r.reservation_id, r.address, r.prefix_len, r.type, "
2391  "r.dhcp6_iaid "
2392  "FROM hosts AS h "
2393  "LEFT JOIN dhcp6_options AS o "
2394  "ON h.host_id = o.host_id "
2395  "LEFT JOIN ipv6_reservations AS r "
2396  "ON h.host_id = r.host_id "
2397  "WHERE h.host_id = "
2398  "( SELECT host_id FROM ipv6_reservations "
2399  "WHERE address = ? AND prefix_len = ? ) "
2400  "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2401 
2402  // Retrieves host information, IPv6 reservations and DHCPv6 options
2403  // associated with a host using IPv6 subnet id and prefix. This query
2404  // returns host information for a single host. However, multiple rows
2405  // are returned due to left joining IPv6 reservations and DHCPv6 options.
2406  // The number of rows returned is multiplication of number of existing
2407  // IPv6 reservations and DHCPv6 options.
2408  {MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
2409  "SELECT h.host_id, h.dhcp_identifier, "
2410  "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2411  "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2412  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2413  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2414  "h.dhcp4_boot_file_name, h.auth_key, "
2415  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2416  "o.persistent, o.user_context, "
2417  "r.reservation_id, r.address, r.prefix_len, r.type, "
2418  "r.dhcp6_iaid "
2419  "FROM hosts AS h "
2420  "LEFT JOIN dhcp6_options AS o "
2421  "ON h.host_id = o.host_id "
2422  "LEFT JOIN ipv6_reservations AS r "
2423  "ON h.host_id = r.host_id "
2424  "WHERE h.dhcp6_subnet_id = ? AND r.address = ? "
2425  "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2426 
2427  // Retrieves host information along with the DHCPv4 options associated with
2428  // it. Left joining the dhcp4_options table results in multiple rows being
2429  // returned for the same host. Hosts are retrieved by IPv4 subnet id.
2430  {MySqlHostDataSourceImpl::GET_HOST_SUBID4,
2431  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2432  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2433  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2434  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2435  "h.dhcp4_boot_file_name, h.auth_key, "
2436  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2437  "o.persistent, o.user_context "
2438  "FROM hosts AS h "
2439  "LEFT JOIN dhcp4_options AS o "
2440  "ON h.host_id = o.host_id "
2441  "WHERE h.dhcp4_subnet_id = ? "
2442  "ORDER BY h.host_id, o.option_id"},
2443 
2444  // Retrieves host information, IPv6 reservations and DHCPv6 options
2445  // associated with a host. The number of rows returned is a multiplication
2446  // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2447  // by IPv6 subnet id.
2448  {MySqlHostDataSourceImpl::GET_HOST_SUBID6,
2449  "SELECT h.host_id, h.dhcp_identifier, "
2450  "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2451  "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2452  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2453  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2454  "h.dhcp4_boot_file_name, h.auth_key, "
2455  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2456  "o.persistent, o.user_context, "
2457  "r.reservation_id, r.address, r.prefix_len, r.type, "
2458  "r.dhcp6_iaid "
2459  "FROM hosts AS h "
2460  "LEFT JOIN dhcp6_options AS o "
2461  "ON h.host_id = o.host_id "
2462  "LEFT JOIN ipv6_reservations AS r "
2463  "ON h.host_id = r.host_id "
2464  "WHERE h.dhcp6_subnet_id = ? "
2465  "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2466 
2467  // Retrieves host information, IPv6 reservations and both DHCPv4 and
2468  // DHCPv6 options associated with the host. The LEFT JOIN clause is used
2469  // to retrieve information from 4 different tables using a single query.
2470  // Hence, this query returns multiple rows for a single host.
2471  {MySqlHostDataSourceImpl::GET_HOST_HOSTNAME,
2472  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2473  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
2474  "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
2475  "h.user_context, "
2476  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2477  "h.dhcp4_boot_file_name, h.auth_key, "
2478  "o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
2479  "o4.persistent, o4.user_context, "
2480  "o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
2481  "o6.persistent, o6.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 dhcp4_options AS o4 "
2486  "ON h.host_id = o4.host_id "
2487  "LEFT JOIN dhcp6_options AS o6 "
2488  "ON h.host_id = o6.host_id "
2489  "LEFT JOIN ipv6_reservations AS r "
2490  "ON h.host_id = r.host_id "
2491  "WHERE h.hostname = ? "
2492  "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"},
2493 
2494  // Retrieves host information and DHCPv4 options using hostname and
2495  // subnet identifier. Left joining the dhcp4_options table results in
2496  // multiple rows being returned for the same host.
2497  {MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4,
2498  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2499  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2500  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2501  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2502  "h.dhcp4_boot_file_name, h.auth_key, "
2503  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2504  "o.persistent, o.user_context "
2505  "FROM hosts AS h "
2506  "LEFT JOIN dhcp4_options AS o "
2507  "ON h.host_id = o.host_id "
2508  "WHERE h.hostname = ? AND h.dhcp4_subnet_id = ? "
2509  "ORDER BY h.host_id, o.option_id"},
2510 
2511  // Retrieves host information, IPv6 reservations and DHCPv6 options
2512  // using hostname and subnet identifier. The number of rows returned
2513  // is a multiplication of number of IPv6 reservations and DHCPv6 options.
2514  {MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6,
2515  "SELECT h.host_id, h.dhcp_identifier, "
2516  "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2517  "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2518  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2519  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2520  "h.dhcp4_boot_file_name, h.auth_key, "
2521  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2522  "o.persistent, o.user_context, "
2523  "r.reservation_id, r.address, r.prefix_len, r.type, "
2524  "r.dhcp6_iaid "
2525  "FROM hosts AS h "
2526  "LEFT JOIN dhcp6_options AS o "
2527  "ON h.host_id = o.host_id "
2528  "LEFT JOIN ipv6_reservations AS r "
2529  "ON h.host_id = r.host_id "
2530  "WHERE h.hostname = ? AND h.dhcp6_subnet_id = ? "
2531  "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2532 
2533  // Retrieves host information along with the DHCPv4 options associated with
2534  // it. Left joining the dhcp4_options table results in multiple rows being
2535  // returned for the same host. Hosts are retrieved by IPv4 subnet id
2536  // and with a host id greater than the start one.
2537  // The number of hosts returned is lower or equal to the limit.
2538  {MySqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE,
2539  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2540  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2541  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2542  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2543  "h.dhcp4_boot_file_name, h.auth_key, "
2544  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2545  "o.persistent, o.user_context "
2546  "FROM ( SELECT * FROM hosts AS h "
2547  "WHERE h.dhcp4_subnet_id = ? AND h.host_id > ? "
2548  "ORDER BY h.host_id "
2549  "LIMIT ? ) AS h "
2550  "LEFT JOIN dhcp4_options AS o "
2551  "ON h.host_id = o.host_id "
2552  "ORDER BY h.host_id, o.option_id"},
2553 
2554  // Retrieves host information, IPv6 reservations and DHCPv6 options
2555  // associated with a host. The number of rows returned is a multiplication
2556  // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2557  // by IPv6 subnet id and with a host id greater than the start one.
2558  // The number of hosts returned is lower or equal to the limit.
2559  {MySqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE,
2560  "SELECT h.host_id, h.dhcp_identifier, "
2561  "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2562  "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2563  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2564  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2565  "h.dhcp4_boot_file_name, h.auth_key, "
2566  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2567  "o.persistent, o.user_context, "
2568  "r.reservation_id, r.address, r.prefix_len, r.type, "
2569  "r.dhcp6_iaid "
2570  "FROM ( SELECT * FROM hosts AS h "
2571  "WHERE h.dhcp6_subnet_id = ? AND h.host_id > ? "
2572  "ORDER BY h.host_id "
2573  "LIMIT ? ) AS h "
2574  "LEFT JOIN dhcp6_options AS o "
2575  "ON h.host_id = o.host_id "
2576  "LEFT JOIN ipv6_reservations AS r "
2577  "ON h.host_id = r.host_id "
2578  "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2579 
2580  // Retrieves host information along with the DHCPv4 options associated with
2581  // it. Left joining the dhcp4_options table results in multiple rows being
2582  // returned for the same host. Hosts are retrieved with a host id greater
2583  // than the start one.
2584  // The number of hosts returned is lower or equal to the limit.
2585  {MySqlHostDataSourceImpl::GET_HOST_PAGE4,
2586  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2587  "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2588  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2589  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2590  "h.dhcp4_boot_file_name, h.auth_key, "
2591  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2592  "o.persistent, o.user_context "
2593  "FROM ( SELECT * FROM hosts AS h "
2594  "WHERE h.host_id > ? "
2595  "ORDER BY h.host_id "
2596  "LIMIT ? ) AS h "
2597  "LEFT JOIN dhcp4_options AS o "
2598  "ON h.host_id = o.host_id "
2599  "ORDER BY h.host_id, o.option_id"},
2600 
2601  // Retrieves host information, IPv6 reservations and DHCPv6 options
2602  // associated with a host. The number of rows returned is a multiplication
2603  // of number of IPv6 reservations and DHCPv6 options. Hosts are retrieved
2604  // with a host id greater than the start one.
2605  // The number of hosts returned is lower or equal to the limit.
2606  {MySqlHostDataSourceImpl::GET_HOST_PAGE6,
2607  "SELECT h.host_id, h.dhcp_identifier, "
2608  "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2609  "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2610  "h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2611  "h.dhcp4_next_server, h.dhcp4_server_hostname, "
2612  "h.dhcp4_boot_file_name, h.auth_key, "
2613  "o.option_id, o.code, o.value, o.formatted_value, o.space, "
2614  "o.persistent, o.user_context, "
2615  "r.reservation_id, r.address, r.prefix_len, r.type, "
2616  "r.dhcp6_iaid "
2617  "FROM ( SELECT * FROM hosts AS h "
2618  "WHERE h.host_id > ? "
2619  "ORDER BY h.host_id "
2620  "LIMIT ? ) 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  "ORDER BY h.host_id, o.option_id, r.reservation_id"},
2626 
2627  // Inserts a host into the 'hosts' table without checking that there is
2628  // a reservation for the IP address.
2629  {MySqlHostDataSourceImpl::INSERT_HOST_NON_UNIQUE_IP,
2630  "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2631  "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2632  "dhcp4_client_classes, dhcp6_client_classes, "
2633  "user_context, dhcp4_next_server, "
2634  "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2635  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
2636 
2637  // Inserts a host into the 'hosts' table with checking that reserved IP
2638  // address is unique. The innermost query checks if there is at least
2639  // one host for the given IP/subnet combination. For checking whether
2640  // hosts exists or not it doesn't matter if we select actual columns,
2641  // thus SELECT 1 was used as an optimization to avoid selecting data
2642  // that will be ignored anyway. If the host does not exist the new
2643  // host is inserted. DUAL is a special MySQL table from which we can
2644  // select the values to be inserted. If the host with the given IP
2645  // address already exists the new host won't be inserted. The caller
2646  // can check the number of affected rows to detect that there was
2647  // a duplicate host in the database.
2648  {MySqlHostDataSourceImpl::INSERT_HOST_UNIQUE_IP,
2649  "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
2650  "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2651  "dhcp4_client_classes, dhcp6_client_classes, "
2652  "user_context, dhcp4_next_server, "
2653  "dhcp4_server_hostname, dhcp4_boot_file_name, auth_key) "
2654  "SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? FROM DUAL "
2655  "WHERE NOT EXISTS ("
2656  "SELECT 1 FROM hosts "
2657  "WHERE ipv4_address = ? AND dhcp4_subnet_id = ? "
2658  "LIMIT 1"
2659  ")"},
2660 
2661  // Inserts a single IPv6 reservation into 'reservations' table without
2662  // checking that the inserted reservation is unique.
2663  {MySqlHostDataSourceImpl::INSERT_V6_RESRV_NON_UNIQUE,
2664  "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2665  "dhcp6_iaid, host_id) "
2666  "VALUES (?, ?, ?, ?, ?)"},
2667 
2668  // Inserts a single IPv6 reservation into 'reservations' table with
2669  // checking that the inserted reservation is unique.
2670  {MySqlHostDataSourceImpl::INSERT_V6_RESRV_UNIQUE,
2671  "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2672  "dhcp6_iaid, host_id) "
2673  "SELECT ?, ?, ?, ?, ? FROM DUAL "
2674  "WHERE NOT EXISTS ("
2675  "SELECT 1 FROM ipv6_reservations "
2676  "WHERE address = ? AND prefix_len = ? "
2677  "LIMIT 1"
2678  ")"},
2679 
2680  // Inserts a single DHCPv4 option into 'dhcp4_options' table.
2681  // Using fixed scope_id = 3, which associates an option with host.
2682  {MySqlHostDataSourceImpl::INSERT_V4_HOST_OPTION,
2683  "INSERT INTO dhcp4_options(option_id, code, value, formatted_value, space, "
2684  "persistent, user_context, dhcp4_subnet_id, host_id, scope_id) "
2685  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2686 
2687  // Inserts a single DHCPv6 option into 'dhcp6_options' table.
2688  // Using fixed scope_id = 3, which associates an option with host.
2689  {MySqlHostDataSourceImpl::INSERT_V6_HOST_OPTION,
2690  "INSERT INTO dhcp6_options(option_id, code, value, formatted_value, space, "
2691  "persistent, user_context, dhcp6_subnet_id, host_id, scope_id) "
2692  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 3)"},
2693 
2694  // Delete IPv4 reservations by subnet id and reserved address.
2695  {MySqlHostDataSourceImpl::DEL_HOST_ADDR4,
2696  "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND ipv4_address = ?"},
2697 
2698  // Delete IPv6 reservations by subnet id and reserved address/prefix.
2699  {MySqlHostDataSourceImpl::DEL_HOST_ADDR6,
2700  "DELETE h FROM hosts AS h "
2701  "INNER JOIN ipv6_reservations AS r "
2702  "ON h.host_id = r.host_id "
2703  "WHERE h.dhcp6_subnet_id = ? AND r.address = ?"},
2704 
2705  // Delete a single IPv4 reservation by subnet id and identifier.
2706  {MySqlHostDataSourceImpl::DEL_HOST_SUBID4_ID,
2707  "DELETE FROM hosts WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type=? "
2708  "AND dhcp_identifier = ?"},
2709 
2710  // Delete a single IPv6 reservation by subnet id and identifier.
2711  {MySqlHostDataSourceImpl::DEL_HOST_SUBID6_ID,
2712  "DELETE FROM hosts WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type=? "
2713  "AND dhcp_identifier = ?"}
2714  }
2715 };
2716 
2717 } // namespace
2718 
2719 // MySqlHostContext Constructor
2720 
2721 MySqlHostContext::MySqlHostContext(const DatabaseConnection::ParameterMap& parameters,
2722  IOServiceAccessorPtr io_service_accessor,
2723  db::DbCallback db_reconnect_callback)
2724  : conn_(parameters, io_service_accessor, db_reconnect_callback),
2725  is_readonly_(true) {
2726 }
2727 
2728 // MySqlHostContextAlloc Constructor and Destructor
2729 
2731  MySqlHostDataSourceImpl& mgr) : ctx_(), mgr_(mgr) {
2732 
2733  if (MultiThreadingMgr::instance().getMode()) {
2734  // multi-threaded
2735  {
2736  // we need to protect the whole pool_ operation, hence extra scope {}
2737  lock_guard<mutex> lock(mgr_.pool_->mutex_);
2738  if (!mgr_.pool_->pool_.empty()) {
2739  ctx_ = mgr_.pool_->pool_.back();
2740  mgr_.pool_->pool_.pop_back();
2741  }
2742  }
2743  if (!ctx_) {
2744  ctx_ = mgr_.createContext();
2745  }
2746  } else {
2747  // single-threaded
2748  if (mgr_.pool_->pool_.empty()) {
2749  isc_throw(Unexpected, "No available MySQL host context?!");
2750  }
2751  ctx_ = mgr_.pool_->pool_.back();
2752  }
2753 }
2754 
2756  if (MultiThreadingMgr::instance().getMode()) {
2757  // multi-threaded
2758  lock_guard<mutex> lock(mgr_.pool_->mutex_);
2759  mgr_.pool_->pool_.push_back(ctx_);
2760  if (ctx_->conn_.isUnusable()) {
2761  mgr_.unusable_ = true;
2762  }
2763  } else if (ctx_->conn_.isUnusable()) {
2764  mgr_.unusable_ = true;
2765  }
2766 }
2767 
2769  : parameters_(parameters), ip_reservations_unique_(true), unusable_(false),
2770  timer_name_("") {
2771 
2772  // Create unique timer name per instance.
2773  timer_name_ = "MySqlHostMgr[";
2774  timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
2775  timer_name_ += "]DbReconnectTimer";
2776 
2777  // Validate the schema version first.
2778  std::pair<uint32_t, uint32_t> code_version(MYSQL_SCHEMA_VERSION_MAJOR,
2780  std::pair<uint32_t, uint32_t> db_version = getVersion();
2781  if (code_version != db_version) {
2783  "MySQL schema version mismatch: need version: "
2784  << code_version.first << "." << code_version.second
2785  << " found version: " << db_version.first << "."
2786  << db_version.second);
2787  }
2788 
2789  // Create an initial context.
2790  pool_.reset(new MySqlHostContextPool());
2791  pool_->pool_.push_back(createContext());
2792 }
2793 
2794 // Create context.
2795 
2801 
2802  // Open the database.
2803  ctx->conn_.openDatabase();
2804 
2805  // Check if we have TLS when we required it.
2806  if (ctx->conn_.getTls()) {
2807  std::string cipher = ctx->conn_.getTlsCipher();
2808  if (cipher.empty()) {
2810  } else {
2813  .arg(cipher);
2814  }
2815  }
2816 
2817  // Prepare query statements. Those are will be only used to retrieve
2818  // information from the database, so they can be used even if the
2819  // database is read only for the current user.
2820  ctx->conn_.prepareStatements(tagged_statements.begin(),
2821  tagged_statements.begin() + WRITE_STMTS_BEGIN);
2822 
2823  // Check if the backend is explicitly configured to operate with
2824  // read only access to the database.
2825  ctx->is_readonly_ = ctx->conn_.configuredReadOnly();
2826 
2827  // If we are using read-write mode for the database we also prepare
2828  // statements for INSERTS etc.
2829  if (!ctx->is_readonly_) {
2830  // Prepare statements for writing to the database, e.g. INSERT.
2831  ctx->conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
2832  tagged_statements.end());
2833  } else {
2835  }
2836 
2837  // Create the exchange objects for use in exchanging data between the
2838  // program and the database.
2839  ctx->host_ipv4_exchange_.reset(new MySqlHostWithOptionsExchange(MySqlHostWithOptionsExchange::DHCP4_ONLY));
2840  ctx->host_ipv6_exchange_.reset(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP6_ONLY));
2841  ctx->host_ipv46_exchange_.reset(new MySqlHostIPv6Exchange(MySqlHostWithOptionsExchange::DHCP4_AND_DHCP6));
2842  ctx->host_ipv6_reservation_exchange_.reset(new MySqlIPv6ReservationExchange());
2843  ctx->host_option_exchange_.reset(new MySqlOptionExchange());
2844 
2845  // Create ReconnectCtl for this connection.
2846  ctx->conn_.makeReconnectCtl(timer_name_);
2847 
2848  return (ctx);
2849 }
2850 
2852 }
2853 
2854 bool
2857 
2858  // Invoke application layer connection lost callback.
2859  if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
2860  return (false);
2861  }
2862 
2863  bool reopened = false;
2864 
2865  const std::string timer_name = db_reconnect_ctl->timerName();
2866 
2867  // At least one connection was lost.
2868  try {
2869  CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
2870  std::list<std::string> host_db_access_list = cfg_db->getHostDbAccessStringList();
2871  for (std::string& hds : host_db_access_list) {
2872  auto parameters = DatabaseConnection::parse(hds);
2873  if (HostMgr::delBackend("mysql", hds, true)) {
2874  HostMgr::addBackend(hds);
2875  }
2876  }
2877  reopened = true;
2878  } catch (const std::exception& ex) {
2880  .arg(ex.what());
2881  }
2882 
2883  if (reopened) {
2884  // Cancel the timer.
2885  if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2886  TimerMgr::instance()->unregisterTimer(timer_name);
2887  }
2888 
2889  // Invoke application layer connection recovered callback.
2890  if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
2891  return (false);
2892  }
2893  } else {
2894  if (!db_reconnect_ctl->checkRetries()) {
2895  // We're out of retries, log it and initiate shutdown.
2897  .arg(db_reconnect_ctl->maxRetries());
2898 
2899  // Cancel the timer.
2900  if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2901  TimerMgr::instance()->unregisterTimer(timer_name);
2902  }
2903 
2904  // Invoke application layer connection failed callback.
2906  return (false);
2907  }
2908 
2910  .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
2911  .arg(db_reconnect_ctl->maxRetries())
2912  .arg(db_reconnect_ctl->retryInterval());
2913 
2914  // Start the timer.
2915  if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
2916  TimerMgr::instance()->registerTimer(timer_name,
2917  std::bind(&MySqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl),
2918  db_reconnect_ctl->retryInterval(),
2920  }
2921  TimerMgr::instance()->setup(timer_name);
2922  }
2923 
2924  return (true);
2925 }
2926 
2927 std::pair<uint32_t, uint32_t>
2931 
2933 }
2934 
2935 void
2937  StatementIndex stindex,
2938  std::vector<MYSQL_BIND>& bind) {
2939  // Bind the parameters to the statement
2940  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], &bind[0]);
2941  checkError(ctx, status, stindex, "unable to bind parameters");
2942 
2943  // Execute the statement
2944  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2945 
2946  if (status != 0) {
2947  // Failure: check for the special case of duplicate entry.
2948  if (mysql_errno(ctx->conn_.mysql_) == ER_DUP_ENTRY) {
2949  isc_throw(DuplicateEntry, "Database duplicate entry error");
2950  }
2951  checkError(ctx, status, stindex, "unable to execute");
2952  }
2953 
2954  // If the number of rows inserted is 0 it means that the query detected
2955  // an attempt to insert duplicated data for which there is no unique
2956  // index in the database. Unique indexes are not created in the database
2957  // when it may be sometimes allowed to insert duplicated records per
2958  // server's configuration.
2959  my_ulonglong numrows = mysql_stmt_affected_rows(ctx->conn_.statements_[stindex]);
2960  if (numrows == 0) {
2961  isc_throw(DuplicateEntry, "Database duplicate entry error");
2962  }
2963 }
2964 
2965 bool
2967  StatementIndex stindex,
2968  MYSQL_BIND* bind) {
2969  // Bind the parameters to the statement
2970  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], &bind[0]);
2971  checkError(ctx, status, stindex, "unable to bind parameters");
2972 
2973  // Execute the statement
2974  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2975 
2976  if (status != 0) {
2977  checkError(ctx, status, stindex, "unable to execute");
2978  }
2979 
2980  // Let's check how many hosts were deleted.
2981  my_ulonglong numrows = mysql_stmt_affected_rows(ctx->conn_.statements_[stindex]);
2982 
2983  return (numrows != 0);
2984 }
2985 
2986 void
2988  const IPv6Resrv& resv,
2989  const HostID& id) {
2990  std::vector<MYSQL_BIND> bind = ctx->host_ipv6_reservation_exchange_->
2991  createBindForSend(resv, id, ip_reservations_unique_);
2992 
2994 }
2995 
2996 void
2998  const StatementIndex& stindex,
2999  const OptionDescriptor& opt_desc,
3000  const std::string& opt_space,
3001  const Optional<SubnetID>& subnet_id,
3002  const HostID& id) {
3003  std::vector<MYSQL_BIND> bind = ctx->host_option_exchange_->createBindForSend(opt_desc, opt_space, subnet_id, id);
3004 
3005  addStatement(ctx, stindex, bind);
3006 }
3007 
3008 void
3010  const StatementIndex& stindex,
3011  const ConstCfgOptionPtr& options_cfg,
3012  const uint64_t host_id) {
3013  // Get option space names and vendor space names and combine them within a
3014  // single list.
3015  std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
3016  std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
3017  option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
3018  vendor_spaces.end());
3019 
3020  // For each option space retrieve all options and insert them into the
3021  // database.
3022  for (auto space = option_spaces.begin(); space != option_spaces.end(); ++space) {
3023  OptionContainerPtr options = options_cfg->getAll(*space);
3024  if (options && !options->empty()) {
3025  for (auto opt = options->begin(); opt != options->end(); ++opt) {
3026  addOption(ctx, stindex, *opt, *space, Optional<SubnetID>(), host_id);
3027  }
3028  }
3029  }
3030 }
3031 
3032 void
3034  const int status,
3035  const StatementIndex index,
3036  const char* what) const {
3037  ctx->conn_.checkError(status, index, what);
3038 }
3039 
3040 void
3042  StatementIndex stindex,
3043  MYSQL_BIND* bind,
3044  boost::shared_ptr<MySqlHostExchange> exchange,
3045  ConstHostCollection& result,
3046  bool single) const {
3047 
3048  // Bind the selection parameters to the statement
3049  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], bind);
3050  checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
3051 
3052  // Set up the MYSQL_BIND array for the data being returned and bind it to
3053  // the statement.
3054  std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
3055  status = mysql_stmt_bind_result(ctx->conn_.statements_[stindex], &outbind[0]);
3056  checkError(ctx, status, stindex, "unable to bind SELECT clause parameters");
3057 
3058  // Execute the statement
3059  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
3060  checkError(ctx, status, stindex, "unable to execute");
3061 
3062  // Ensure that all the lease information is retrieved in one go to avoid
3063  // overhead of going back and forth between client and server.
3064  status = mysql_stmt_store_result(ctx->conn_.statements_[stindex]);
3065  checkError(ctx, status, stindex, "unable to set up for storing all results");
3066 
3067  // Set up the fetch "release" object to release resources associated
3068  // with the call to mysql_stmt_fetch when this method exits, then
3069  // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
3070  // successful data fetch.
3071  MySqlFreeResult fetch_release(ctx->conn_.statements_[stindex]);
3072  while ((status = mysql_stmt_fetch(ctx->conn_.statements_[stindex])) ==
3074  try {
3075  exchange->processFetchedData(result);
3076 
3077  } catch (const isc::BadValue& ex) {
3078  // Rethrow the exception with a bit more data.
3079  isc_throw(BadValue, ex.what() << ". Statement is <" <<
3080  ctx->conn_.text_statements_[stindex] << ">");
3081  }
3082 
3083  if (single && (result.size() > 1)) {
3084  isc_throw(MultipleRecords, "multiple records were found in the "
3085  "database where only one was expected for query "
3086  << ctx->conn_.text_statements_[stindex]);
3087  }
3088  }
3089 
3090  // How did the fetch end?
3091  // If mysql_stmt_fetch return value is equal to 1 an error occurred.
3092  if (status == MLM_MYSQL_FETCH_FAILURE) {
3093  // Error - unable to fetch results
3094  checkError(ctx, status, stindex, "unable to fetch results");
3095 
3096  } else if (status == MYSQL_DATA_TRUNCATED) {
3097  // Data truncated - throw an exception indicating what was at fault
3098  isc_throw(DataTruncated, ctx->conn_.text_statements_[stindex]
3099  << " returned truncated data: columns affected are "
3100  << exchange->getErrorColumns());
3101  }
3102 }
3103 
3106  const SubnetID& subnet_id,
3107  const Host::IdentifierType& identifier_type,
3108  const uint8_t* identifier_begin,
3109  const size_t identifier_len,
3110  StatementIndex stindex,
3111  boost::shared_ptr<MySqlHostExchange> exchange) const {
3112 
3113  // Set up the WHERE clause value
3114  MYSQL_BIND inbind[3];
3115  memset(inbind, 0, sizeof(inbind));
3116 
3117  uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3118  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3119  inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3120  inbind[0].is_unsigned = MLM_TRUE;
3121 
3122  // Identifier value.
3123  std::vector<char> identifier_vec(identifier_begin,
3124  identifier_begin + identifier_len);
3125  unsigned long length = identifier_vec.size();
3126  inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3127  inbind[2].buffer = &identifier_vec[0];
3128  inbind[2].buffer_length = length;
3129  inbind[2].length = &length;
3130 
3131  // Identifier type.
3132  char identifier_type_copy = static_cast<char>(identifier_type);
3133  inbind[1].buffer_type = MYSQL_TYPE_TINY;
3134  inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
3135  inbind[1].is_unsigned = MLM_TRUE;
3136 
3137  ConstHostCollection collection;
3138  getHostCollection(ctx, stindex, inbind, exchange, collection, true);
3139 
3140  // Return single record if present, else clear the host.
3141  ConstHostPtr result;
3142  if (!collection.empty()) {
3143  result = *collection.begin();
3144  }
3145 
3146  return (result);
3147 }
3148 
3149 void
3151  if (ctx->is_readonly_) {
3152  isc_throw(ReadOnlyDb, "MySQL host database backend is configured to"
3153  " operate in read only mode");
3154  }
3155 }
3156 
3158  : impl_(new MySqlHostDataSourceImpl(parameters)) {
3159 }
3160 
3162 }
3163 
3166  return (impl_->parameters_);
3167 }
3168 
3169 void
3171  // Get a context
3172  MySqlHostContextAlloc get_context(*impl_);
3173  MySqlHostContextPtr ctx = get_context.ctx_;
3174 
3175  // If operating in read-only mode, throw exception.
3176  impl_->checkReadOnly(ctx);
3177 
3178  // Initiate MySQL transaction as we will have to make multiple queries
3179  // to insert host information into multiple tables. If that fails on
3180  // any stage, the transaction will be rolled back by the destructor of
3181  // the MySqlTransaction class.
3182  MySqlTransaction transaction(ctx->conn_);
3183 
3184  // If we're configured to check that an IP reservation within a given subnet
3185  // is unique, the IP reservation exists and the subnet is actually set
3186  // we will be using a special query that checks for uniqueness. Otherwise,
3187  // we will use a regular insert statement.
3188  bool unique_ip = impl_->ip_reservations_unique_ && !host->getIPv4Reservation().isV4Zero()
3189  && host->getIPv4SubnetID() != SUBNET_ID_UNUSED;
3190 
3191  // Create the MYSQL_BIND array for the host
3192  std::vector<MYSQL_BIND> bind = ctx->host_ipv4_exchange_->createBindForSend(host, unique_ip);
3193 
3194  // ... and insert the host.
3195  impl_->addStatement(ctx, unique_ip ? MySqlHostDataSourceImpl::INSERT_HOST_UNIQUE_IP :
3197 
3198  // Gets the last inserted hosts id
3199  uint64_t host_id = mysql_insert_id(ctx->conn_.mysql_);
3200 
3201  // Insert DHCPv4 options.
3202  ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
3203  if (cfg_option4) {
3204  impl_->addOptions(ctx, MySqlHostDataSourceImpl::INSERT_V4_HOST_OPTION,
3205  cfg_option4, host_id);
3206  }
3207 
3208  // Insert DHCPv6 options.
3209  ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
3210  if (cfg_option6) {
3211  impl_->addOptions(ctx, MySqlHostDataSourceImpl::INSERT_V6_HOST_OPTION,
3212  cfg_option6, host_id);
3213  }
3214 
3215  // Insert IPv6 reservations.
3216  IPv6ResrvRange v6resv = host->getIPv6Reservations();
3217  if (std::distance(v6resv.first, v6resv.second) > 0) {
3218  for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
3219  ++resv) {
3220  impl_->addResv(ctx, resv->second, host_id);
3221  }
3222  }
3223 
3224  // Everything went fine, so explicitly commit the transaction.
3225  transaction.commit();
3226 }
3227 
3228 bool
3230  const asiolink::IOAddress& addr) {
3231  // Get a context
3232  MySqlHostContextAlloc get_context(*impl_);
3233  MySqlHostContextPtr ctx = get_context.ctx_;
3234 
3235  // If operating in read-only mode, throw exception.
3236  impl_->checkReadOnly(ctx);
3237 
3238  // Set up the WHERE clause value
3239  MYSQL_BIND inbind[2];
3240 
3241  uint32_t subnet = subnet_id;
3242  memset(inbind, 0, sizeof(inbind));
3243  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3244  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3245  inbind[0].is_unsigned = MLM_TRUE;
3246 
3247  // v4
3248  if (addr.isV4()) {
3249  uint32_t addr4 = addr.toUint32();
3250  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3251  inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3252  inbind[1].is_unsigned = MLM_TRUE;
3253 
3254  return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_ADDR4, inbind));
3255  }
3256 
3257  // v6
3258  std::string addr_str = addr.toText();
3259  unsigned long addr_len = addr_str.size();
3260  inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3261  inbind[1].buffer = reinterpret_cast<char*>
3262  (const_cast<char*>(addr_str.c_str()));
3263  inbind[1].length = &addr_len;
3264  inbind[1].buffer_length = addr_len;
3265 
3266  return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_ADDR6, inbind));
3267 }
3268 
3269 bool
3271  const Host::IdentifierType& identifier_type,
3272  const uint8_t* identifier_begin,
3273  const size_t identifier_len) {
3274  // Get a context
3275  MySqlHostContextAlloc get_context(*impl_);
3276  MySqlHostContextPtr ctx = get_context.ctx_;
3277 
3278  // If operating in read-only mode, throw exception.
3279  impl_->checkReadOnly(ctx);
3280 
3281  // Set up the WHERE clause value
3282  MYSQL_BIND inbind[3];
3283 
3284  // subnet-id
3285  memset(inbind, 0, sizeof(inbind));
3286  uint32_t subnet = static_cast<uint32_t>(subnet_id);
3287  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3288  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3289  inbind[0].is_unsigned = MLM_TRUE;
3290 
3291  // identifier type
3292  char identifier_type_copy = static_cast<char>(identifier_type);
3293  inbind[1].buffer_type = MYSQL_TYPE_TINY;
3294  inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
3295  inbind[1].is_unsigned = MLM_TRUE;
3296 
3297  // identifier value
3298  std::vector<char> identifier_vec(identifier_begin,
3299  identifier_begin + identifier_len);
3300  unsigned long length = identifier_vec.size();
3301  inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3302  inbind[2].buffer = &identifier_vec[0];
3303  inbind[2].buffer_length = length;
3304  inbind[2].length = &length;
3305 
3306  ConstHostCollection collection;
3307  return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_SUBID4_ID, inbind));
3308 }
3309 
3310 bool
3312  const Host::IdentifierType& identifier_type,
3313  const uint8_t* identifier_begin,
3314  const size_t identifier_len) {
3315  // Get a context
3316  MySqlHostContextAlloc get_context(*impl_);
3317  MySqlHostContextPtr ctx = get_context.ctx_;
3318 
3319  // If operating in read-only mode, throw exception.
3320  impl_->checkReadOnly(ctx);
3321 
3322  // Set up the WHERE clause value
3323  MYSQL_BIND inbind[3];
3324 
3325  // subnet-id
3326  memset(inbind, 0, sizeof(inbind));
3327  uint32_t subnet = static_cast<uint32_t>(subnet_id);
3328  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3329  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3330  inbind[0].is_unsigned = MLM_TRUE;
3331 
3332  // identifier type
3333  char identifier_type_copy = static_cast<char>(identifier_type);
3334  inbind[1].buffer_type = MYSQL_TYPE_TINY;
3335  inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
3336  inbind[1].is_unsigned = MLM_TRUE;
3337 
3338  // identifier value
3339  std::vector<char> identifier_vec(identifier_begin,
3340  identifier_begin + identifier_len);
3341  unsigned long length = identifier_vec.size();
3342  inbind[2].buffer_type = MYSQL_TYPE_BLOB;
3343  inbind[2].buffer = &identifier_vec[0];
3344  inbind[2].buffer_length = length;
3345  inbind[2].length = &length;
3346 
3347  ConstHostCollection collection;
3348  return (impl_->delStatement(ctx, MySqlHostDataSourceImpl::DEL_HOST_SUBID6_ID, inbind));
3349 }
3350 
3353  const uint8_t* identifier_begin,
3354  const size_t identifier_len) const {
3355  // Get a context
3356  MySqlHostContextAlloc get_context(*impl_);
3357  MySqlHostContextPtr ctx = get_context.ctx_;
3358 
3359  // Set up the WHERE clause value
3360  MYSQL_BIND inbind[2];
3361  memset(inbind, 0, sizeof(inbind));
3362 
3363  // Identifier type.
3364  char identifier_type_copy = static_cast<char>(identifier_type);
3365  inbind[1].buffer = &identifier_type_copy;
3366  inbind[1].buffer_type = MYSQL_TYPE_TINY;
3367  inbind[1].is_unsigned = MLM_TRUE;
3368 
3369  // Identifier value.
3370  std::vector<char> identifier_vec(identifier_begin,
3371  identifier_begin + identifier_len);
3372  unsigned long int length = identifier_vec.size();
3373  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
3374  inbind[0].buffer = &identifier_vec[0];
3375  inbind[0].buffer_length = length;
3376  inbind[0].length = &length;
3377 
3378  ConstHostCollection result;
3379  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_DHCPID, inbind,
3380  ctx->host_ipv46_exchange_, result, false);
3381 
3382  return (result);
3383 }
3384 
3386 MySqlHostDataSource::getAll4(const SubnetID& subnet_id) const {
3387  // Get a context
3388  MySqlHostContextAlloc get_context(*impl_);
3389  MySqlHostContextPtr ctx = get_context.ctx_;
3390 
3391  // Set up the WHERE clause value
3392  MYSQL_BIND inbind[1];
3393  memset(inbind, 0, sizeof(inbind));
3394  uint32_t subnet = subnet_id;
3395  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3396  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3397  inbind[0].is_unsigned = MLM_TRUE;
3398 
3399  ConstHostCollection result;
3400  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID4, inbind,
3401  ctx->host_ipv4_exchange_, result, false);
3402 
3403  return (result);
3404 }
3405 
3407 MySqlHostDataSource::getAll6(const SubnetID& subnet_id) const {
3408  // Get a context
3409  MySqlHostContextAlloc get_context(*impl_);
3410  MySqlHostContextPtr ctx = get_context.ctx_;
3411 
3412  // Set up the WHERE clause value
3413  MYSQL_BIND inbind[1];
3414  memset(inbind, 0, sizeof(inbind));
3415  uint32_t subnet = subnet_id;
3416  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3417  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3418  inbind[0].is_unsigned = MLM_TRUE;
3419 
3420  ConstHostCollection result;
3421  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6, inbind,
3422  ctx->host_ipv6_exchange_, result, false);
3423 
3424  return (result);
3425 }
3426 
3428 MySqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
3429  // Get a context
3430  MySqlHostContextAlloc get_context(*impl_);
3431  MySqlHostContextPtr ctx = get_context.ctx_;
3432 
3433  // Set up the WHERE clause value
3434  MYSQL_BIND inbind[1];
3435  memset(inbind, 0, sizeof(inbind));
3436 
3437  // Hostname
3438  char hostname_[HOSTNAME_MAX_LEN];
3439  strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3440  unsigned long length = hostname.length();
3441  inbind[0].buffer_type = MYSQL_TYPE_STRING;
3442  inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3443  inbind[0].buffer_length = length;
3444  inbind[0].length = &length;
3445 
3446  ConstHostCollection result;
3447  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME, inbind,
3448  ctx->host_ipv46_exchange_, result, false);
3449 
3450  return (result);
3451 }
3452 
3454 MySqlHostDataSource::getAllbyHostname4(const std::string& hostname,
3455  const SubnetID& subnet_id) const {
3456  // Get a context
3457  MySqlHostContextAlloc get_context(*impl_);
3458  MySqlHostContextPtr ctx = get_context.ctx_;
3459 
3460  // Set up the WHERE clause value
3461  MYSQL_BIND inbind[2];
3462  memset(inbind, 0, sizeof(inbind));
3463 
3464  // Hostname
3465  char hostname_[HOSTNAME_MAX_LEN];
3466  strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3467  unsigned long length = hostname.length();
3468  inbind[0].buffer_type = MYSQL_TYPE_STRING;
3469  inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3470  inbind[0].buffer_length = length;
3471  inbind[0].length = &length;
3472 
3473  // Subnet ID
3474  uint32_t subnet = subnet_id;
3475  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3476  inbind[1].buffer = reinterpret_cast<char*>(&subnet);
3477  inbind[1].is_unsigned = MLM_TRUE;
3478 
3479  ConstHostCollection result;
3480  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4, inbind,
3481  ctx->host_ipv4_exchange_, result, false);
3482 
3483  return (result);
3484 }
3485 
3487 MySqlHostDataSource::getAllbyHostname6(const std::string& hostname,
3488  const SubnetID& subnet_id) const {
3489  // Get a context
3490  MySqlHostContextAlloc get_context(*impl_);
3491  MySqlHostContextPtr ctx = get_context.ctx_;
3492 
3493  // Set up the WHERE clause value
3494  MYSQL_BIND inbind[2];
3495  memset(inbind, 0, sizeof(inbind));
3496 
3497  // Hostname
3498  char hostname_[HOSTNAME_MAX_LEN];
3499  strncpy(hostname_, hostname.c_str(), HOSTNAME_MAX_LEN - 1);
3500  unsigned long length = hostname.length();
3501  inbind[0].buffer_type = MYSQL_TYPE_STRING;
3502  inbind[0].buffer = reinterpret_cast<char*>(hostname_);
3503  inbind[0].buffer_length = length;
3504  inbind[0].length = &length;
3505 
3506  // Subnet ID
3507  uint32_t subnet = subnet_id;
3508  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3509  inbind[1].buffer = reinterpret_cast<char*>(&subnet);
3510  inbind[1].is_unsigned = MLM_TRUE;
3511 
3512  ConstHostCollection result;
3513  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6, inbind,
3514  ctx->host_ipv6_exchange_, result, false);
3515 
3516  return (result);
3517 }
3518 
3521  size_t& /*source_index*/,
3522  uint64_t lower_host_id,
3523  const HostPageSize& page_size) const {
3524  // Get a context
3525  MySqlHostContextAlloc get_context(*impl_);
3526  MySqlHostContextPtr ctx = get_context.ctx_;
3527 
3528  // Set up the WHERE clause value
3529  MYSQL_BIND inbind[3];
3530  memset(inbind, 0, sizeof(inbind));
3531 
3532  // Bind subnet id
3533  uint32_t subnet = subnet_id;
3534  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3535  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3536  inbind[0].is_unsigned = MLM_TRUE;
3537 
3538  // Bind lower host id
3539  uint32_t host_id = lower_host_id;
3540  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3541  inbind[1].buffer = reinterpret_cast<char*>(&host_id);
3542  inbind[1].is_unsigned = MLM_TRUE;
3543 
3544  // Bind page size value
3545  uint32_t page_size_data = page_size.page_size_;
3546  inbind[2].buffer_type = MYSQL_TYPE_LONG;
3547  inbind[2].buffer = reinterpret_cast<char*>(&page_size_data);
3548  inbind[2].is_unsigned = MLM_TRUE;
3549 
3550  ConstHostCollection result;
3551  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE, inbind,
3552  ctx->host_ipv4_exchange_, result, false);
3553 
3554  return (result);
3555 }
3556 
3559  size_t& /*source_index*/,
3560  uint64_t lower_host_id,
3561  const HostPageSize& page_size) const {
3562  // Get a context
3563  MySqlHostContextAlloc get_context(*impl_);
3564  MySqlHostContextPtr ctx = get_context.ctx_;
3565 
3566  // Set up the WHERE clause value
3567  MYSQL_BIND inbind[3];
3568  memset(inbind, 0, sizeof(inbind));
3569 
3570  // Bind subnet id
3571  uint32_t subnet = subnet_id;
3572  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3573  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3574  inbind[0].is_unsigned = MLM_TRUE;
3575 
3576  // Bind lower host id
3577  uint32_t host_id = lower_host_id;
3578  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3579  inbind[1].buffer = reinterpret_cast<char*>(&host_id);
3580  inbind[1].is_unsigned = MLM_TRUE;
3581 
3582  // Bind page size value
3583  uint32_t page_size_data = page_size.page_size_;
3584  inbind[2].buffer_type = MYSQL_TYPE_LONG;
3585  inbind[2].buffer = reinterpret_cast<char*>(&page_size_data);
3586  inbind[2].is_unsigned = MLM_TRUE;
3587 
3588  ConstHostCollection result;
3589  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE, inbind,
3590  ctx->host_ipv6_exchange_, result, false);
3591 
3592  return (result);
3593 }
3594 
3596 MySqlHostDataSource::getPage4(size_t& /*source_index*/,
3597  uint64_t lower_host_id,
3598  const HostPageSize& page_size) const {
3599  // Get a context
3600  MySqlHostContextAlloc get_context(*impl_);
3601  MySqlHostContextPtr ctx = get_context.ctx_;
3602 
3603  // Set up the WHERE clause value
3604  MYSQL_BIND inbind[2];
3605  memset(inbind, 0, sizeof(inbind));
3606 
3607  // Bind lower host id
3608  uint32_t host_id = lower_host_id;
3609  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3610  inbind[0].buffer = reinterpret_cast<char*>(&host_id);
3611  inbind[0].is_unsigned = MLM_TRUE;
3612 
3613  // Bind page size value
3614  uint32_t page_size_data = page_size.page_size_;
3615  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3616  inbind[1].buffer = reinterpret_cast<char*>(&page_size_data);
3617  inbind[1].is_unsigned = MLM_TRUE;
3618 
3619  ConstHostCollection result;
3620  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PAGE4, inbind,
3621  ctx->host_ipv4_exchange_, result, false);
3622 
3623  return (result);
3624 }
3625 
3627 MySqlHostDataSource::getPage6(size_t& /*source_index*/,
3628  uint64_t lower_host_id,
3629  const HostPageSize& page_size) const {
3630  // Get a context
3631  MySqlHostContextAlloc get_context(*impl_);
3632  MySqlHostContextPtr ctx = get_context.ctx_;
3633 
3634  // Set up the WHERE clause value
3635  MYSQL_BIND inbind[2];
3636  memset(inbind, 0, sizeof(inbind));
3637 
3638  // Bind lower host id
3639  uint32_t host_id = lower_host_id;
3640  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3641  inbind[0].buffer = reinterpret_cast<char*>(&host_id);
3642  inbind[0].is_unsigned = MLM_TRUE;
3643 
3644  // Bind page size value
3645  uint32_t page_size_data = page_size.page_size_;
3646  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3647  inbind[1].buffer = reinterpret_cast<char*>(&page_size_data);
3648  inbind[1].is_unsigned = MLM_TRUE;
3649 
3650  ConstHostCollection result;
3651  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PAGE6, inbind,
3652  ctx->host_ipv6_exchange_, result, false);
3653 
3654  return (result);
3655 }
3656 
3659  // Get a context
3660  MySqlHostContextAlloc get_context(*impl_);
3661  MySqlHostContextPtr ctx = get_context.ctx_;
3662 
3663  // Set up the WHERE clause value
3664  MYSQL_BIND inbind[1];
3665  memset(inbind, 0, sizeof(inbind));
3666 
3667  uint32_t addr4 = address.toUint32();
3668  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3669  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
3670  inbind[0].is_unsigned = MLM_TRUE;
3671 
3672  ConstHostCollection result;
3673  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_ADDR, inbind,
3674  ctx->host_ipv4_exchange_, result, false);
3675 
3676  return (result);
3677 }
3678 
3681  const Host::IdentifierType& identifier_type,
3682  const uint8_t* identifier_begin,
3683  const size_t identifier_len) const {
3684  // Get a context
3685  MySqlHostContextAlloc get_context(*impl_);
3686  MySqlHostContextPtr ctx = get_context.ctx_;
3687 
3688  return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3690  ctx->host_ipv4_exchange_));
3691 }
3692 
3695  const asiolink::IOAddress& address) const {
3696  if (!address.isV4()) {
3697  isc_throw(BadValue, "MySqlHostDataSource::get4(id, address): "
3698  "wrong address type, address supplied is an IPv6 address");
3699  }
3700 
3701  // Get a context
3702  MySqlHostContextAlloc get_context(*impl_);
3703  MySqlHostContextPtr ctx = get_context.ctx_;
3704 
3705  // Set up the WHERE clause value
3706  MYSQL_BIND inbind[2];
3707  uint32_t subnet = subnet_id;
3708  memset(inbind, 0, sizeof(inbind));
3709  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3710  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3711  inbind[0].is_unsigned = MLM_TRUE;
3712 
3713  uint32_t addr4 = address.toUint32();
3714  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3715  inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3716  inbind[1].is_unsigned = MLM_TRUE;
3717 
3718  ConstHostCollection collection;
3719  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
3720  ctx->host_ipv4_exchange_, collection, true);
3721 
3722  // Return single record if present, else clear the host.
3723  ConstHostPtr result;
3724  if (!collection.empty()) {
3725  result = *collection.begin();
3726  }
3727 
3728  return (result);
3729 }
3730 
3733  const asiolink::IOAddress& address) const {
3734  if (!address.isV4()) {
3735  isc_throw(BadValue, "MySqlHostDataSource::getAll4(id, address): "
3736  "wrong address type, address supplied is an IPv6 address");
3737  }
3738 
3739  // Get a context
3740  MySqlHostContextAlloc get_context(*impl_);
3741  MySqlHostContextPtr ctx = get_context.ctx_;
3742 
3743  // Set up the WHERE clause value
3744  MYSQL_BIND inbind[2];
3745  uint32_t subnet = subnet_id;
3746  memset(inbind, 0, sizeof(inbind));
3747  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3748  inbind[0].buffer = reinterpret_cast<char*>(&subnet);
3749  inbind[0].is_unsigned = MLM_TRUE;
3750 
3751  uint32_t addr4 = address.toUint32();
3752  inbind[1].buffer_type = MYSQL_TYPE_LONG;
3753  inbind[1].buffer = reinterpret_cast<char*>(&addr4);
3754  inbind[1].is_unsigned = MLM_TRUE;
3755 
3756  ConstHostCollection collection;
3757  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
3758  ctx->host_ipv4_exchange_, collection, false);
3759  return (collection);
3760 }
3761 
3764  const Host::IdentifierType& identifier_type,
3765  const uint8_t* identifier_begin,
3766  const size_t identifier_len) const {
3767  // Get a context
3768  MySqlHostContextAlloc get_context(*impl_);
3769  MySqlHostContextPtr ctx = get_context.ctx_;
3770 
3771  return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3773  ctx->host_ipv6_exchange_));
3774 }
3775 
3778  const uint8_t prefix_len) const {
3779  if (!prefix.isV6()) {
3780  isc_throw(BadValue, "MySqlHostDataSource::get6(prefix, prefix_len): "
3781  "wrong address type, address supplied is an IPv4 address");
3782  }
3783 
3784  // Get a context
3785  MySqlHostContextAlloc get_context(*impl_);
3786  MySqlHostContextPtr ctx = get_context.ctx_;
3787 
3788  // Set up the WHERE clause value
3789  MYSQL_BIND inbind[2];
3790  memset(inbind, 0, sizeof(inbind));
3791 
3792  std::string addr6 = prefix.toText();
3793  unsigned long addr6_length = addr6.size();
3794 
3795  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
3796  inbind[0].buffer = reinterpret_cast<char*>
3797  (const_cast<char*>(addr6.c_str()));
3798  inbind[0].length = &addr6_length;
3799  inbind[0].buffer_length = addr6_length;
3800 
3801  uint8_t tmp = prefix_len;
3802  inbind[1].buffer_type = MYSQL_TYPE_TINY;
3803  inbind[1].buffer = reinterpret_cast<char*>(&tmp);
3804  inbind[1].is_unsigned = MLM_TRUE;
3805 
3806  ConstHostCollection collection;
3807  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_PREFIX, inbind,
3808  ctx->host_ipv6_exchange_, collection, true);
3809 
3810  // Return single record if present, else clear the host.
3811  ConstHostPtr result;
3812  if (!collection.empty()) {
3813  result = *collection.begin();
3814  }
3815 
3816  return (result);
3817 }
3818 
3821  const asiolink::IOAddress& address) const {
3822  if (!address.isV6()) {
3823  isc_throw(BadValue, "MySqlHostDataSource::get6(id, address): "
3824  "wrong address type, address supplied is an IPv4 address");
3825  }
3826 
3827  // Get a context
3828  MySqlHostContextAlloc get_context(*impl_);
3829  MySqlHostContextPtr ctx = get_context.ctx_;
3830 
3831  // Set up the WHERE clause value
3832  MYSQL_BIND inbind[2];
3833  memset(inbind, 0, sizeof(inbind));
3834 
3835  uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3836  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3837  inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3838  inbind[0].is_unsigned = MLM_TRUE;
3839 
3840  std::string addr6 = address.toText();
3841  unsigned long addr6_length = addr6.size();
3842 
3843  inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3844  inbind[1].buffer = reinterpret_cast<char*>
3845  (const_cast<char*>(addr6.c_str()));
3846  inbind[1].length = &addr6_length;
3847  inbind[1].buffer_length = addr6_length;
3848 
3849  ConstHostCollection collection;
3850  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
3851  ctx->host_ipv6_exchange_, collection, true);
3852 
3853  // Return single record if present, else clear the host.
3854  ConstHostPtr result;
3855  if (!collection.empty()) {
3856  result = *collection.begin();
3857  }
3858 
3859  return (result);
3860 }
3861 
3864  const asiolink::IOAddress& address) const {
3865  if (!address.isV6()) {
3866  isc_throw(BadValue, "MySqlHostDataSource::getAll6(id, address): "
3867  "wrong address type, address supplied is an IPv4 address");
3868  }
3869 
3870  // Get a context
3871  MySqlHostContextAlloc get_context(*impl_);
3872  MySqlHostContextPtr ctx = get_context.ctx_;
3873 
3874  // Set up the WHERE clause value
3875  MYSQL_BIND inbind[2];
3876  memset(inbind, 0, sizeof(inbind));
3877 
3878  uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
3879  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3880  inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
3881  inbind[0].is_unsigned = MLM_TRUE;
3882 
3883  std::string addr6 = address.toText();
3884  unsigned long addr6_length = addr6.size();
3885 
3886  inbind[1].buffer_type = MYSQL_TYPE_BLOB;
3887  inbind[1].buffer = reinterpret_cast<char*>
3888  (const_cast<char*>(addr6.c_str()));
3889  inbind[1].length = &addr6_length;
3890  inbind[1].buffer_length = addr6_length;
3891 
3892  ConstHostCollection collection;
3893  impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
3894  ctx->host_ipv6_exchange_, collection, false);
3895  return (collection);
3896 }
3897 
3898 // Miscellaneous database methods.
3899 
3900 std::string
3902  std::string name = "";
3903  // Get a context
3904  MySqlHostContextAlloc get_context(*impl_);
3905  MySqlHostContextPtr ctx = get_context.ctx_;
3906 
3907  try {
3908  name = ctx->conn_.getParameter("name");
3909  } catch (...) {
3910  // Return an empty name
3911  }
3912  return (name);
3913 }
3914 
3915 std::string
3917  return (std::string("Host data source that stores host information"
3918  "in MySQL database"));
3919 }
3920 
3921 std::pair<uint32_t, uint32_t>
3923  return(impl_->getVersion());
3924 }
3925 
3926 void
3928  // Get a context
3929  MySqlHostContextAlloc get_context(*impl_);
3930  MySqlHostContextPtr ctx = get_context.ctx_;
3931 
3932  // If operating in read-only mode, throw exception.
3933  impl_->checkReadOnly(ctx);
3934  if (ctx->conn_.isTransactionStarted()) {
3935  ctx->conn_.commit();
3936  }
3937 }
3938 
3939 void
3941  // Get a context
3942  MySqlHostContextAlloc get_context(*impl_);
3943  MySqlHostContextPtr ctx = get_context.ctx_;
3944 
3945  // If operating in read-only mode, throw exception.
3946  impl_->checkReadOnly(ctx);
3947  if (ctx->conn_.isTransactionStarted()) {
3948  ctx->conn_.rollback();
3949  }
3950 }
3951 
3952 bool
3954  impl_->ip_reservations_unique_ = unique;
3955  return (true);
3956 }
3957 
3958 bool
3960  return (impl_->unusable_);
3961 }
3962 
3963 } // namespace dhcp
3964 } // namespace isc
RAII class creating a critical section.
std::function< bool(util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
const size_t ADDRESS6_TEXT_MAX_LEN
Maximum size of an IPv6 address represented as a text string.
Definition: host.h:32
bool my_bool
my_bool type in MySQL 8.x.
Option descriptor.
Definition: cfg_option.h:46
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:136
static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl)
Attempts to reconnect the server to the host DB backend manager.
const size_t OPTION_FORMATTED_VALUE_MAX_LEN
Maximum length of option value specified in textual format.
Definition: host.h:48
boost::shared_ptr< MySqlHostContextPool > MySqlHostContextPoolPtr
Type of pointers to context pools.
virtual std::string getDescription() const
Returns description of the backend.
Wraps value holding size of the page with host reservations.
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:745
Fetch and Release MySQL Results.
virtual ConstHostCollection getAllbyHostname(const std::string &hostname) const
Return all hosts with a hostname.
const size_t USER_CONTEXT_MAX_LEN
Maximum length of user context.
Definition: host.h:54
bool delStatement(MySqlHostContextPtr &ctx, StatementIndex stindex, MYSQL_BIND *bind)
Executes statements that delete records.
MySqlHostDataSourceImpl(const DatabaseConnection::ParameterMap &parameters)
Constructor.
data::ConstElementPtr getContext() const
Returns const pointer to the user context.
Definition: user_context.h:24
const size_t CLIENT_CLASSES_MAX_LEN
Maximum length of classes stored in a dhcp4/6_client_classes columns.
Definition: host.h:36
virtual void rollback()
Rollback Transactions.
std::vector< MySqlHostContextPtr > pool_
The vector of available contexts.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
uint8_t getPrefixLen() const
Returns prefix length.
Definition: host.h:195
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)
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:47
virtual ConstHostCollection getAllbyHostname6(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv6 subnet.
const size_t BOOT_FILE_NAME_MAX_LEN
Maximum length of the boot file name.
Definition: host.h:60
Data is truncated.
Definition: db_exceptions.h:23
virtual bool setIPReservationsUnique(const bool unique)
Controls whether IP reservations are unique or non-unique.
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition: host.h:801
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap &parameters)
Get the schema version.
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
void commit()
Commits transaction.
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_GET_VERSION
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:748
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
const size_t page_size_
Holds page size.
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)
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
Definition: host_mgr.h:643
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const uint32_t MYSQL_SCHEMA_VERSION_MAJOR
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:83
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
virtual bool isUnusable()
Flag which indicates if the host manager has at least one unusable connection.
STL namespace.
void addStatement(MySqlHostContextPtr &ctx, MySqlHostDataSourceImpl::StatementIndex stindex, std::vector< MYSQL_BIND > &bind)
Executes statements which inserts a row into one of the tables.
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_ATTEMPT_SCHEDULE
const uint32_t MYSQL_SCHEMA_VERSION_MINOR
std::string timer_name_
Timer name used to register database reconnect timer.
static ParameterMap parse(const std::string &dbaccess)
Parse database access string.
void addResv(MySqlHostContextPtr &ctx, const IPv6Resrv &resv, const HostID &id)
Inserts IPv6 Reservation into ipv6_reservation table.
bool unusable_
Indicates if there is at least one connection that can no longer be used for normal operations...
IPv6 reservation for a host.
Definition: host.h:161
const size_t OPTION_SPACE_MAX_LEN
Maximum length of option space name.
Definition: host.h:51
std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
MySqlConnection conn_
MySQL connection.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
Exception thrown on failure to open database.
const size_t SERVER_HOSTNAME_MAX_LEN
Maximum length of the server hostname.
Definition: host.h:57
const size_t HOSTNAME_MAX_LEN
Maximum length of the hostname stored in DNS.
Definition: host.h:42
virtual isc::db::DatabaseConnection::ParameterMap getParameters() const
Return backend parameters.
void checkError(MySqlHostContextPtr &ctx, const int status, const StatementIndex index, const char *what) const
Check Error and Throw Exception.
int MysqlExecuteStatement(MYSQL_STMT *stmt)
Execute a prepared statement.
Multiple lease records found where one expected.
Definition: db_exceptions.h:16
DatabaseConnection::ParameterMap parameters_
The parameters.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
std::string formatted_value_
Option value in textual (CSV) format.
Definition: cfg_option.h:70
virtual ConstHostCollection getAll4(const SubnetID &subnet_id) const
Return all hosts in a DHCPv4 subnet.
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&#39;s unique identifier.
Definition: edns.h:19
const my_bool MLM_FALSE
MySQL false value.
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:243
boost::shared_ptr< MySqlHostIPv6Exchange > host_ipv46_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts, DHCPv4 and DHCPv6 options, and IPv6 reservations using a single query.
static bool delBackend(const std::string &db_type)
Delete an alternate host backend (aka host data source).
Definition: host_mgr.cc:53
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:807
boost::shared_ptr< MySqlHostWithOptionsExchange > host_ipv4_exchange_
The exchange objects are used for transfer of data to/from the database.
bool persistent_
Persistence flag.
Definition: cfg_option.h:55
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.
A generic exception that is thrown when an unexpected error condition occurs.
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_ATTEMPT_FAILED
virtual void commit()
Commit Transactions.
virtual ~MySqlHostDataSource()
Virtual destructor.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:804
virtual std::string getName() const
Returns backend name.
void checkReadOnly(MySqlHostContextPtr &ctx) const
Throws exception if database is read only.
virtual ConstHostCollection getAll6(const SubnetID &subnet_id) const
Return all hosts in a DHCPv6 subnet.
void addOptions(MySqlHostContextPtr &ctx, const StatementIndex &stindex, const ConstCfgOptionPtr &options_cfg, const uint64_t host_id)
Inserts multiple options into the database.
static bool invokeDbRecoveredCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection&#39;s restored connectivity callback.
Represents a device with IPv4 and/or IPv6 reservations.
Definition: host.h:297
Type
Type of the reservation.
Definition: host.h:167
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.
IPv6ResrvCollection::const_iterator IPv6ResrvIterator
Definition: host.h:241
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_READONLY
OptionPtr option_
Option instance.
Definition: cfg_option.h:49
const int MLM_MYSQL_FETCH_SUCCESS
check for bool size
const isc::log::MessageID DHCPSRV_MYSQL_NO_TLS
Authentication keys.
Definition: host.h:75
Defines the logger used by the top-level component of kea-lfc.
uint16_t code_
Type getType() const
Returns reservation type.
Definition: host.h:204
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...
bool ip_reservations_unique_
Holds the setting whether the IP reservations must be unique or may be non-unique.
const isc::log::MessageID DHCPSRV_MYSQL_HOST_DB_RECONNECT_FAILED
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.
MySqlHostContextPoolPtr pool_
The pool of contexts.
static bool invokeDbFailedCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection&#39;s restore failed connectivity callback.
virtual std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
Implementation of the MySqlHostDataSource.
MySqlHostContextAlloc(MySqlHostDataSourceImpl &mgr)
Constructor.
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.
const int MLM_MYSQL_FETCH_FAILURE
MySQL fetch failure code.
#define DHCP6_OPTION_SPACE
RAII object representing MySQL transaction.
#define DHCP4_OPTION_SPACE
global std option spaces
static void addBackend(const std::string &access)
Add an alternate host backend (aka host data source).
Definition: host_mgr.cc:48
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:276
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
bool is_readonly_
Indicates if the database is opened in read only mode.
boost::shared_ptr< MySqlHostContext > MySqlHostContextPtr
Type of pointers to contexts.
const int DHCPSRV_DBG_TRACE
DHCP server library logging levels.
Definition: dhcpsrv_log.h:26
Attempt to modify data in read-only database.
Definition: db_exceptions.h:44
boost::shared_ptr< MySqlHostIPv6Exchange > host_ipv6_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts, DHCPv6 options and IPv6 reservations.
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
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...
boost::shared_ptr< MySqlIPv6ReservationExchange > host_ipv6_reservation_exchange_
Pointer to an object representing an exchange which can be used to insert new IPv6 reservation...
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
const isc::log::MessageID DHCPSRV_MYSQL_TLS_CIPHER
IdentifierType
Type of the host identifier.
Definition: host.h:307
std::function< isc::asiolink::IOServicePtr()> IOServiceAccessor
Function which returns the IOService that can be used to recover the connection.
const size_t TEXT_AUTH_KEY_LEN
Maximum length of authentication keys (coded in hexadecimal).
Definition: host.h:66
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition: timer_mgr.cc:449
static bool invokeDbLostCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection&#39;s lost connectivity callback.
static const StatementIndex WRITE_STMTS_BEGIN
Index of first statement performing write to the database.
const size_t OPTION_VALUE_MAX_LEN
Maximum length of option value.
Definition: host.h:45
uint64_t HostID
HostID (used only when storing in MySQL or PostgreSQL backends)
Definition: host.h:69
A template representing an optional value.
Definition: optional.h:36
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.
std::mutex mutex_
The mutex to protect pool access.
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 ...
const asiolink::IOAddress & getPrefix() const
Returns prefix for the reservation.
Definition: host.h:190
virtual bool del(const SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete hosts by (subnet-id, address)
Exception thrown on failure to execute a database function.
const my_bool MLM_TRUE
MySQL true value.
MySqlHostDataSource(const db::DatabaseConnection::ParameterMap &parameters)
Constructor.
MySqlHostContextPtr createContext() const
Create a new context.
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
Database duplicate entry error.
Definition: db_exceptions.h:30
virtual void add(const HostPtr &host)
Adds a new host to the collection.
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition: subnet_id.h:24
Common MySQL Connector Pool.
virtual ConstHostCollection getAllbyHostname4(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv4 subnet.