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