Kea  1.9.9-git
mysql_lease_mgr.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2021 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_address.h>
10 #include <dhcp/duid.h>
11 #include <dhcp/hwaddr.h>
12 #include <dhcpsrv/cfg_db_access.h>
13 #include <dhcpsrv/cfgmgr.h>
14 #include <dhcpsrv/dhcpsrv_log.h>
17 #include <dhcpsrv/timer_mgr.h>
18 #include <mysql/mysql_connection.h>
20 
21 #include <boost/array.hpp>
22 #include <boost/make_shared.hpp>
23 #include <boost/static_assert.hpp>
24 #include <mysqld_error.h>
25 
26 #include <iostream>
27 #include <iomanip>
28 #include <limits>
29 #include <sstream>
30 #include <string>
31 #include <time.h>
32 
33 using namespace isc;
34 using namespace isc::asiolink;
35 using namespace isc::db;
36 using namespace isc::dhcp;
37 using namespace isc::data;
38 using namespace isc::util;
39 using namespace std;
40 
82 
83 namespace {
84 
89 const size_t HOSTNAME_MAX_LEN = 255;
90 
95 const size_t ADDRESS6_TEXT_MAX_LEN = 39;
96 
98 const size_t USER_CONTEXT_MAX_LEN = 8192;
99 
100 boost::array<TaggedStatement, MySqlLeaseMgr::NUM_STATEMENTS>
101 tagged_statements = { {
102  {MySqlLeaseMgr::DELETE_LEASE4,
103  "DELETE FROM lease4 WHERE address = ? AND expire = ?"},
104  {MySqlLeaseMgr::DELETE_LEASE4_STATE_EXPIRED,
105  "DELETE FROM lease4 "
106  "WHERE state = ? AND expire < ?"},
107  {MySqlLeaseMgr::DELETE_LEASE6,
108  "DELETE FROM lease6 WHERE address = ? AND expire = ?"},
109  {MySqlLeaseMgr::DELETE_LEASE6_STATE_EXPIRED,
110  "DELETE FROM lease6 "
111  "WHERE state = ? AND expire < ?"},
112  {MySqlLeaseMgr::GET_LEASE4,
113  "SELECT address, hwaddr, client_id, "
114  "valid_lifetime, expire, subnet_id, "
115  "fqdn_fwd, fqdn_rev, hostname, "
116  "state, user_context "
117  "FROM lease4"},
118  {MySqlLeaseMgr::GET_LEASE4_ADDR,
119  "SELECT address, hwaddr, client_id, "
120  "valid_lifetime, expire, subnet_id, "
121  "fqdn_fwd, fqdn_rev, hostname, "
122  "state, user_context "
123  "FROM lease4 "
124  "WHERE address = ?"},
125  {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
126  "SELECT address, hwaddr, client_id, "
127  "valid_lifetime, expire, subnet_id, "
128  "fqdn_fwd, fqdn_rev, hostname, "
129  "state, user_context "
130  "FROM lease4 "
131  "WHERE client_id = ?"},
132  {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
133  "SELECT address, hwaddr, client_id, "
134  "valid_lifetime, expire, subnet_id, "
135  "fqdn_fwd, fqdn_rev, hostname, "
136  "state, user_context "
137  "FROM lease4 "
138  "WHERE client_id = ? AND subnet_id = ?"},
139  {MySqlLeaseMgr::GET_LEASE4_HWADDR,
140  "SELECT address, hwaddr, client_id, "
141  "valid_lifetime, expire, subnet_id, "
142  "fqdn_fwd, fqdn_rev, hostname, "
143  "state, user_context "
144  "FROM lease4 "
145  "WHERE hwaddr = ?"},
146  {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
147  "SELECT address, hwaddr, client_id, "
148  "valid_lifetime, expire, subnet_id, "
149  "fqdn_fwd, fqdn_rev, hostname, "
150  "state, user_context "
151  "FROM lease4 "
152  "WHERE hwaddr = ? AND subnet_id = ?"},
153  {MySqlLeaseMgr::GET_LEASE4_PAGE,
154  "SELECT address, hwaddr, client_id, "
155  "valid_lifetime, expire, subnet_id, "
156  "fqdn_fwd, fqdn_rev, hostname, "
157  "state, user_context "
158  "FROM lease4 "
159  "WHERE address > ? "
160  "ORDER BY address "
161  "LIMIT ?"},
162  {MySqlLeaseMgr::GET_LEASE4_SUBID,
163  "SELECT address, hwaddr, client_id, "
164  "valid_lifetime, expire, subnet_id, "
165  "fqdn_fwd, fqdn_rev, hostname, "
166  "state, user_context "
167  "FROM lease4 "
168  "WHERE subnet_id = ?"},
169  {MySqlLeaseMgr::GET_LEASE4_HOSTNAME,
170  "SELECT address, hwaddr, client_id, "
171  "valid_lifetime, expire, subnet_id, "
172  "fqdn_fwd, fqdn_rev, hostname, "
173  "state, user_context "
174  "FROM lease4 "
175  "WHERE hostname = ?"},
176  {MySqlLeaseMgr::GET_LEASE4_EXPIRE,
177  "SELECT address, hwaddr, client_id, "
178  "valid_lifetime, expire, subnet_id, "
179  "fqdn_fwd, fqdn_rev, hostname, "
180  "state, user_context "
181  "FROM lease4 "
182  "WHERE state != ? "
183  "AND valid_lifetime != 4294967295 "
184  "AND expire < ? "
185  "ORDER BY expire ASC "
186  "LIMIT ?"},
187  {MySqlLeaseMgr::GET_LEASE6,
188  "SELECT address, duid, valid_lifetime, "
189  "expire, subnet_id, pref_lifetime, "
190  "lease_type, iaid, prefix_len, "
191  "fqdn_fwd, fqdn_rev, hostname, "
192  "hwaddr, hwtype, hwaddr_source, "
193  "state, user_context "
194  "FROM lease6"},
195  {MySqlLeaseMgr::GET_LEASE6_ADDR,
196  "SELECT address, duid, valid_lifetime, "
197  "expire, subnet_id, pref_lifetime, "
198  "lease_type, iaid, prefix_len, "
199  "fqdn_fwd, fqdn_rev, hostname, "
200  "hwaddr, hwtype, hwaddr_source, "
201  "state, user_context "
202  "FROM lease6 "
203  "WHERE address = ? AND lease_type = ?"},
204  {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
205  "SELECT address, duid, valid_lifetime, "
206  "expire, subnet_id, pref_lifetime, "
207  "lease_type, iaid, prefix_len, "
208  "fqdn_fwd, fqdn_rev, hostname, "
209  "hwaddr, hwtype, hwaddr_source, "
210  "state, user_context "
211  "FROM lease6 "
212  "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
213  {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
214  "SELECT address, duid, valid_lifetime, "
215  "expire, subnet_id, pref_lifetime, "
216  "lease_type, iaid, prefix_len, "
217  "fqdn_fwd, fqdn_rev, hostname, "
218  "hwaddr, hwtype, hwaddr_source, "
219  "state, user_context "
220  "FROM lease6 "
221  "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
222  "AND lease_type = ?"},
223  {MySqlLeaseMgr::GET_LEASE6_PAGE,
224  "SELECT address, duid, valid_lifetime, "
225  "expire, subnet_id, pref_lifetime, "
226  "lease_type, iaid, prefix_len, "
227  "fqdn_fwd, fqdn_rev, hostname, "
228  "hwaddr, hwtype, hwaddr_source, "
229  "state, user_context "
230  "FROM lease6 "
231  "WHERE address > ? "
232  "ORDER BY address "
233  "LIMIT ?"},
234  {MySqlLeaseMgr::GET_LEASE6_SUBID,
235  "SELECT address, duid, valid_lifetime, "
236  "expire, subnet_id, pref_lifetime, "
237  "lease_type, iaid, prefix_len, "
238  "fqdn_fwd, fqdn_rev, hostname, "
239  "hwaddr, hwtype, hwaddr_source, "
240  "state, user_context "
241  "FROM lease6 "
242  "WHERE subnet_id = ?"},
243  {MySqlLeaseMgr::GET_LEASE6_DUID,
244  "SELECT address, duid, valid_lifetime, "
245  "expire, subnet_id, pref_lifetime, "
246  "lease_type, iaid, prefix_len, "
247  "fqdn_fwd, fqdn_rev, hostname, "
248  "hwaddr, hwtype, hwaddr_source, "
249  "state, user_context "
250  "FROM lease6 "
251  "WHERE duid = ?"},
252  {MySqlLeaseMgr::GET_LEASE6_HOSTNAME,
253  "SELECT address, duid, valid_lifetime, "
254  "expire, subnet_id, pref_lifetime, "
255  "lease_type, iaid, prefix_len, "
256  "fqdn_fwd, fqdn_rev, hostname, "
257  "hwaddr, hwtype, hwaddr_source, "
258  "state, user_context "
259  "FROM lease6 "
260  "WHERE hostname = ?"},
261  {MySqlLeaseMgr::GET_LEASE6_EXPIRE,
262  "SELECT address, duid, valid_lifetime, "
263  "expire, subnet_id, pref_lifetime, "
264  "lease_type, iaid, prefix_len, "
265  "fqdn_fwd, fqdn_rev, hostname, "
266  "hwaddr, hwtype, hwaddr_source, "
267  "state, user_context "
268  "FROM lease6 "
269  "WHERE state != ? "
270  "AND valid_lifetime != 4294967295 "
271  "AND expire < ? "
272  "ORDER BY expire ASC "
273  "LIMIT ?"},
274  {MySqlLeaseMgr::INSERT_LEASE4,
275  "INSERT INTO lease4(address, hwaddr, client_id, "
276  "valid_lifetime, expire, subnet_id, "
277  "fqdn_fwd, fqdn_rev, hostname, "
278  "state, user_context) "
279  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
280  {MySqlLeaseMgr::INSERT_LEASE6,
281  "INSERT INTO lease6(address, duid, valid_lifetime, "
282  "expire, subnet_id, pref_lifetime, "
283  "lease_type, iaid, prefix_len, "
284  "fqdn_fwd, fqdn_rev, hostname, "
285  "hwaddr, hwtype, hwaddr_source, "
286  "state, user_context) "
287  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
288  {MySqlLeaseMgr::UPDATE_LEASE4,
289  "UPDATE lease4 SET address = ?, hwaddr = ?, "
290  "client_id = ?, valid_lifetime = ?, expire = ?, "
291  "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
292  "hostname = ?, "
293  "state = ?, user_context = ? "
294  "WHERE address = ? AND expire = ?"},
295  {MySqlLeaseMgr::UPDATE_LEASE6,
296  "UPDATE lease6 SET address = ?, duid = ?, "
297  "valid_lifetime = ?, expire = ?, subnet_id = ?, "
298  "pref_lifetime = ?, lease_type = ?, iaid = ?, "
299  "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
300  "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
301  "state = ?, user_context = ? "
302  "WHERE address = ? AND expire = ?"},
303  {MySqlLeaseMgr::ALL_LEASE4_STATS,
304  "SELECT subnet_id, state, leases as state_count "
305  "FROM lease4_stat ORDER BY subnet_id, state"},
306  {MySqlLeaseMgr::SUBNET_LEASE4_STATS,
307  "SELECT subnet_id, state, leases as state_count "
308  "FROM lease4_stat "
309  "WHERE subnet_id = ? "
310  "ORDER BY state"},
311  {MySqlLeaseMgr::SUBNET_RANGE_LEASE4_STATS,
312  "SELECT subnet_id, state, leases as state_count "
313  "FROM lease4_stat "
314  "WHERE subnet_id >= ? and subnet_id <= ? "
315  "ORDER BY subnet_id, state"},
316  {MySqlLeaseMgr::ALL_LEASE6_STATS,
317  "SELECT subnet_id, lease_type, state, leases as state_count "
318  "FROM lease6_stat ORDER BY subnet_id, lease_type, state"},
319  {MySqlLeaseMgr::SUBNET_LEASE6_STATS,
320  "SELECT subnet_id, lease_type, state, leases as state_count "
321  "FROM lease6_stat "
322  "WHERE subnet_id = ? "
323  "ORDER BY lease_type, state"},
324  {MySqlLeaseMgr::SUBNET_RANGE_LEASE6_STATS,
325  "SELECT subnet_id, lease_type, state, leases as state_count "
326  "FROM lease6_stat "
327  "WHERE subnet_id >= ? and subnet_id <= ? "
328  "ORDER BY subnet_id, lease_type, state"}
329  }
330 };
331 
332 } // namespace
333 
334 namespace isc {
335 namespace dhcp {
336 
343 
345 public:
346 
358  static void setErrorIndicators(MYSQL_BIND* bind, my_bool* error,
359  size_t count) {
360  for (size_t i = 0; i < count; ++i) {
361  error[i] = MLM_FALSE;
362  bind[i].error = reinterpret_cast<my_bool*>(&error[i]);
363  }
364  }
365 
379  static std::string getColumnsInError(my_bool* error, std::string* names,
380  size_t count) {
381  std::string result = "";
382 
383  // Accumulate list of column names
384  for (size_t i = 0; i < count; ++i) {
385  if (error[i] == MLM_TRUE) {
386  if (!result.empty()) {
387  result += ", ";
388  }
389  result += names[i];
390  }
391  }
392 
393  if (result.empty()) {
394  result = "(None)";
395  }
396 
397  return (result);
398  }
399 };
400 
413 
416  static const size_t LEASE_COLUMNS = 11;
417 
418 public:
419 
424  MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), hwaddr_null_(MLM_FALSE),
425  client_id_length_(0), client_id_null_(MLM_FALSE),
426  subnet_id_(0), valid_lifetime_(0),
427  fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0),
428  state_(0), user_context_length_(0),
429  user_context_null_(MLM_FALSE) {
430  memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
431  memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
432  memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
433  memset(user_context_, 0, sizeof(user_context_));
434  std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
435 
436  // Set the column names (for error messages)
437  columns_[0] = "address";
438  columns_[1] = "hwaddr";
439  columns_[2] = "client_id";
440  columns_[3] = "valid_lifetime";
441  columns_[4] = "expire";
442  columns_[5] = "subnet_id";
443  columns_[6] = "fqdn_fwd";
444  columns_[7] = "fqdn_rev";
445  columns_[8] = "hostname";
446  columns_[9] = "state";
447  columns_[10] = "user_context";
448  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
449  }
450 
460  std::vector<MYSQL_BIND> createBindForSend(const Lease4Ptr& lease) {
461 
462  // Store lease object to ensure it remains valid.
463  lease_ = lease;
464 
465  // Initialize prior to constructing the array of MYSQL_BIND structures.
466  // It sets all fields, including is_null, to zero, so we need to set
467  // is_null only if it should be true. This gives up minor performance
468  // benefit while being safe approach. For improved readability, the
469  // code that explicitly sets is_null is there, but is commented out.
470  memset(bind_, 0, sizeof(bind_));
471 
472  // Set up the structures for the various components of the lease4
473  // structure.
474 
475  try {
476  // address: uint32_t
477  // The address in the Lease structure is an IOAddress object. Convert
478  // this to an integer for storage.
479  addr4_ = lease_->addr_.toUint32();
480  bind_[0].buffer_type = MYSQL_TYPE_LONG;
481  bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
482  bind_[0].is_unsigned = MLM_TRUE;
483  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
484  // reasons, see memset() above
485 
486  // hwaddr: varbinary(20) - hardware/MAC address
487  HWAddrPtr hwaddr = lease_->hwaddr_;
488  if (hwaddr) {
489  hwaddr_ = hwaddr->hwaddr_;
490  hwaddr_length_ = hwaddr->hwaddr_.size();
491 
492  // Make sure that the buffer has at least length of 1, even if
493  // empty HW address is passed. This is required by some of the
494  // MySQL connectors that the buffer is set to non-null value.
495  // Otherwise, null value would be inserted into the database,
496  // rather than empty string.
497  if (hwaddr_.empty()) {
498  hwaddr_.resize(1);
499  }
500 
501  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
502  bind_[1].buffer = reinterpret_cast<char*>(&(hwaddr_[0]));
503  bind_[1].buffer_length = hwaddr_length_;
504  bind_[1].length = &hwaddr_length_;
505  } else {
506  bind_[1].buffer_type = MYSQL_TYPE_NULL;
507  // According to http://dev.mysql.com/doc/refman/5.5/en/
508  // c-api-prepared-statement-data-structures.html, the other
509  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
510  // but let's set them to some sane values in case earlier versions
511  // didn't have that assumption.
512  hwaddr_null_ = MLM_TRUE;
513  bind_[1].buffer = NULL;
514  bind_[1].is_null = &hwaddr_null_;
515  }
516 
517  // client_id: varbinary(128)
518  if (lease_->client_id_) {
519  client_id_ = lease_->client_id_->getClientId();
520  client_id_length_ = client_id_.size();
521 
522  // Make sure that the buffer has at least length of 1, even if
523  // empty client id is passed. This is required by some of the
524  // MySQL connectors that the buffer is set to non-null value.
525  // Otherwise, null value would be inserted into the database,
526  // rather than empty string.
527  if (client_id_.empty()) {
528  client_id_.resize(1);
529  }
530 
531  bind_[2].buffer_type = MYSQL_TYPE_BLOB;
532  bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
533  bind_[2].buffer_length = client_id_length_;
534  bind_[2].length = &client_id_length_;
535  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
536  // reasons, see memset() above
537  } else {
538  bind_[2].buffer_type = MYSQL_TYPE_NULL;
539  // According to http://dev.mysql.com/doc/refman/5.5/en/
540  // c-api-prepared-statement-data-structures.html, the other
541  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
542  // but let's set them to some sane values in case earlier versions
543  // didn't have that assumption.
544  client_id_null_ = MLM_TRUE;
545  bind_[2].buffer = NULL;
546  bind_[2].is_null = &client_id_null_;
547  }
548 
549  // valid lifetime: unsigned int
550  bind_[3].buffer_type = MYSQL_TYPE_LONG;
551  bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
552  bind_[3].is_unsigned = MLM_TRUE;
553  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
554  // reasons, see memset() above
555 
556  // expire: timestamp
557  // The lease structure holds the client last transmission time (cltt_)
558  // For convenience for external tools, this is converted to lease
559  // expiry time (expire). The relationship is given by:
560  //
561  // expire = cltt_ + valid_lft_
562  // Avoid overflow
563  uint32_t valid_lft = lease_->valid_lft_;
564  if (valid_lft == Lease::INFINITY_LFT) {
565  valid_lft = 0;
566  }
567  MySqlConnection::convertToDatabaseTime(lease_->cltt_, valid_lft,
568  expire_);
569  bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
570  bind_[4].buffer = reinterpret_cast<char*>(&expire_);
571  bind_[4].buffer_length = sizeof(expire_);
572  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
573  // reasons, see memset() above
574 
575  // subnet_id: unsigned int
576  // Can use lease_->subnet_id_ directly as it is of type uint32_t.
577  bind_[5].buffer_type = MYSQL_TYPE_LONG;
578  bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
579  bind_[5].is_unsigned = MLM_TRUE;
580  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
581  // reasons, see memset() above
582 
583  // fqdn_fwd: boolean
584  bind_[6].buffer_type = MYSQL_TYPE_TINY;
585  bind_[6].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
586  bind_[6].is_unsigned = MLM_TRUE;
587  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
588  // reasons, see memset() above
589 
590  // fqdn_rev: boolean
591  bind_[7].buffer_type = MYSQL_TYPE_TINY;
592  bind_[7].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
593  bind_[7].is_unsigned = MLM_TRUE;
594  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
595  // reasons, see memset() above
596 
597  // hostname: varchar(255)
598  // Note that previously we used MYSQL_TYPE_VARCHAR instead of
599  // MYSQL_TYPE_STRING. However, that caused 'buffer type not supported'
600  // errors on some systems running MariaDB.
601  bind_[8].buffer_type = MYSQL_TYPE_STRING;
602  bind_[8].buffer = const_cast<char*>(lease_->hostname_.c_str());
603  bind_[8].buffer_length = lease_->hostname_.length();
604  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
605  // reasons, see memset() above
606 
607  // state: uint32_t
608  bind_[9].buffer_type = MYSQL_TYPE_LONG;
609  bind_[9].buffer = reinterpret_cast<char*>(&lease_->state_);
610  bind_[9].is_unsigned = MLM_TRUE;
611  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
612  // reasons, see memset() above
613 
614  // user_context: text
615  ConstElementPtr ctx = lease->getContext();
616  if (ctx) {
617  bind_[10].buffer_type = MYSQL_TYPE_STRING;
618  std::string ctx_txt = ctx->str();
619  strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
620  bind_[10].buffer = user_context_;
621  bind_[10].buffer_length = ctx_txt.length();
622  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
623  // reasons, see memset() above
624  } else {
625  bind_[10].buffer_type = MYSQL_TYPE_NULL;
626  }
627 
628  // Add the error flags
629  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
630 
631  // .. and check that we have the numbers correct at compile time.
632  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
633 
634  } catch (const std::exception& ex) {
636  "Could not create bind array from Lease4: "
637  << lease_->addr_.toText() << ", reason: " << ex.what());
638  }
639 
640  // Add the data to the vector. Note the end element is one after the
641  // end of the array.
642  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
643  }
644 
650  std::vector<MYSQL_BIND> createBindForReceive() {
651 
652  // Initialize MYSQL_BIND array.
653  // It sets all fields, including is_null, to zero, so we need to set
654  // is_null only if it should be true. This gives up minor performance
655  // benefit while being safe approach. For improved readability, the
656  // code that explicitly sets is_null is there, but is commented out.
657  memset(bind_, 0, sizeof(bind_));
658 
659  // address: uint32_t
660  bind_[0].buffer_type = MYSQL_TYPE_LONG;
661  bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
662  bind_[0].is_unsigned = MLM_TRUE;
663  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
664  // reasons, see memset() above
665 
666  // hwaddr: varbinary(20)
667  hwaddr_length_ = sizeof(hwaddr_buffer_);
668  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
669  bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
670  bind_[1].buffer_length = hwaddr_length_;
671  bind_[1].length = &hwaddr_length_;
672  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
673  // reasons, see memset() above
674 
675  // client_id: varbinary(128)
676  client_id_length_ = sizeof(client_id_buffer_);
677  bind_[2].buffer_type = MYSQL_TYPE_BLOB;
678  bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
679  bind_[2].buffer_length = client_id_length_;
680  bind_[2].length = &client_id_length_;
681  bind_[2].is_null = &client_id_null_;
682  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
683  // reasons, see memset() above
684 
685  // valid lifetime: unsigned int
686  bind_[3].buffer_type = MYSQL_TYPE_LONG;
687  bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
688  bind_[3].is_unsigned = MLM_TRUE;
689  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
690  // reasons, see memset() above
691 
692  // expire: timestamp
693  bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
694  bind_[4].buffer = reinterpret_cast<char*>(&expire_);
695  bind_[4].buffer_length = sizeof(expire_);
696  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
697  // reasons, see memset() above
698 
699  // subnet_id: unsigned int
700  bind_[5].buffer_type = MYSQL_TYPE_LONG;
701  bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
702  bind_[5].is_unsigned = MLM_TRUE;
703  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
704  // reasons, see memset() above
705 
706  // fqdn_fwd: boolean
707  bind_[6].buffer_type = MYSQL_TYPE_TINY;
708  bind_[6].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
709  bind_[6].is_unsigned = MLM_TRUE;
710  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
711  // reasons, see memset() above
712 
713  // fqdn_rev: boolean
714  bind_[7].buffer_type = MYSQL_TYPE_TINY;
715  bind_[7].buffer = reinterpret_cast<char*>(&fqdn_rev_);
716  bind_[7].is_unsigned = MLM_TRUE;
717  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
718  // reasons, see memset() above
719 
720  // hostname: varchar(255)
721  // Note that previously we used MYSQL_TYPE_VARCHAR instead of
722  // MYSQL_TYPE_STRING. However, that caused 'buffer type not supported'
723  // errors on some systems running MariaDB.
724  hostname_length_ = sizeof(hostname_buffer_);
725  bind_[8].buffer_type = MYSQL_TYPE_STRING;
726  bind_[8].buffer = reinterpret_cast<char*>(hostname_buffer_);
727  bind_[8].buffer_length = hostname_length_;
728  bind_[8].length = &hostname_length_;
729  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
730  // reasons, see memset() above
731 
732  // state: uint32_t
733  bind_[9].buffer_type = MYSQL_TYPE_LONG;
734  bind_[9].buffer = reinterpret_cast<char*>(&state_);
735  bind_[9].is_unsigned = MLM_TRUE;
736  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
737  // reasons, see memset() above
738 
739  // user_context: text
740  user_context_null_ = MLM_FALSE;
741  user_context_length_ = sizeof(user_context_);
742  bind_[10].buffer_type = MYSQL_TYPE_STRING;
743  bind_[10].buffer = reinterpret_cast<char*>(user_context_);
744  bind_[10].buffer_length = user_context_length_;
745  bind_[10].length = &user_context_length_;
746  bind_[10].is_null = &user_context_null_;
747 
748  // Add the error flags
749  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
750 
751  // .. and check that we have the numbers correct at compile time.
752  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
753 
754  // Add the data to the vector. Note the end element is one after the
755  // end of the array.
756  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
757  }
758 
768  // Convert times received from the database to times for the lease
769  // structure
770  time_t cltt = 0;
771  // Recover from overflow
772  uint32_t valid_lft = valid_lifetime_;
773  if (valid_lft == Lease::INFINITY_LFT) {
774  valid_lft = 0;
775  }
776  MySqlConnection::convertFromDatabaseTime(expire_, valid_lft, cltt);
777 
778  if (client_id_null_ == MLM_TRUE) {
779  // There's no client-id, so we pass client-id_length_ set to 0
780  client_id_length_ = 0;
781  }
782 
783  // Hostname is passed to Lease4 as a string object. We have to create
784  // it from the buffer holding hostname and the buffer length.
785  std::string hostname(hostname_buffer_,
786  hostname_buffer_ + hostname_length_);
787 
788  // Set hardware address if it was set
789  HWAddrPtr hwaddr;
790  if (hwaddr_null_ == MLM_FALSE) {
791  hwaddr.reset(new HWAddr(hwaddr_buffer_, hwaddr_length_, HTYPE_ETHER));
792  }
793 
794  // Convert user_context to string as well.
795  std::string user_context;
796  if (user_context_null_ == MLM_FALSE) {
797  user_context_[user_context_length_] = '\0';
798  user_context.assign(user_context_);
799  }
800 
801  // Set the user context if there is one.
802  ConstElementPtr ctx;
803  if (!user_context.empty()) {
804  ctx = Element::fromJSON(user_context);
805  if (!ctx || (ctx->getType() != Element::map)) {
806  isc_throw(BadValue, "user context '" << user_context
807  << "' is not a JSON map");
808  }
809  }
810 
811  Lease4Ptr lease(boost::make_shared<Lease4>(addr4_, hwaddr,
812  client_id_buffer_,
813  client_id_length_,
814  valid_lifetime_, cltt,
815  subnet_id_, fqdn_fwd_,
816  fqdn_rev_, hostname));
817 
818  // Set state.
819  lease->state_ = state_;
820 
821  if (ctx) {
822  lease->setContext(ctx);
823  }
824 
825  return (lease);
826  }
827 
838  std::string getErrorColumns() {
839  return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
840  }
841 
842 private:
843 
844  // Note: All array lengths are equal to the corresponding variable in the
845  // schema.
846  // Note: Arrays are declared fixed length for speed of creation
847  uint32_t addr4_;
848  MYSQL_BIND bind_[LEASE_COLUMNS];
849  std::string columns_[LEASE_COLUMNS];
850  my_bool error_[LEASE_COLUMNS];
851  Lease4Ptr lease_;
852  std::vector<uint8_t> hwaddr_;
853  uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
854  unsigned long hwaddr_length_;
855  my_bool hwaddr_null_;
856  std::vector<uint8_t> client_id_;
857  uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
858  unsigned long client_id_length_;
859  my_bool client_id_null_;
860  MYSQL_TIME expire_;
861  uint32_t subnet_id_;
862  uint32_t valid_lifetime_;
863  my_bool fqdn_fwd_;
864  my_bool fqdn_rev_;
865  char hostname_buffer_[HOSTNAME_MAX_LEN];
866  unsigned long hostname_length_;
867  uint32_t state_;
868  char user_context_[USER_CONTEXT_MAX_LEN];
869  unsigned long user_context_length_;
870  my_bool user_context_null_;
871 };
872 
885 
888  static const size_t LEASE_COLUMNS = 17;
889 
890 public:
891 
896  MySqlLease6Exchange() : addr6_length_(0), hwaddr_length_(0),
897  hwaddr_null_(MLM_FALSE), duid_length_(0),
898  iaid_(0), lease_type_(0), prefix_len_(0),
899  pref_lifetime_(0), subnet_id_(0), valid_lifetime_(0),
900  fqdn_fwd_(false), fqdn_rev_(false),
901  hostname_length_(0), hwtype_(0), hwaddr_source_(0),
902  state_(0), user_context_length_(0),
903  user_context_null_(MLM_FALSE) {
904  memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
905  memset(duid_buffer_, 0, sizeof(duid_buffer_));
906  memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
907  memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
908  memset(user_context_, 0, sizeof(user_context_));
909  std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
910 
911  // Set the column names (for error messages)
912  columns_[0] = "address";
913  columns_[1] = "duid";
914  columns_[2] = "valid_lifetime";
915  columns_[3] = "expire";
916  columns_[4] = "subnet_id";
917  columns_[5] = "pref_lifetime";
918  columns_[6] = "lease_type";
919  columns_[7] = "iaid";
920  columns_[8] = "prefix_len";
921  columns_[9] = "fqdn_fwd";
922  columns_[10] = "fqdn_rev";
923  columns_[11] = "hostname";
924  columns_[12] = "hwaddr";
925  columns_[13] = "hwtype";
926  columns_[14] = "hwaddr_source";
927  columns_[15] = "state";
928  columns_[16] = "user_context";
929  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
930  }
931 
940  std::vector<MYSQL_BIND> createBindForSend(const Lease6Ptr& lease) {
941  // Store lease object to ensure it remains valid.
942  lease_ = lease;
943 
944  // Ensure bind_ array clear for constructing the MYSQL_BIND structures
945  // for this lease.
946  // It sets all fields, including is_null, to zero, so we need to set
947  // is_null only if it should be true. This gives up minor performance
948  // benefit while being safe approach. For improved readability, the
949  // code that explicitly sets is_null is there, but is commented out.
950  memset(bind_, 0, sizeof(bind_));
951 
952  try {
953  // address: varchar(39)
954  addr6_ = lease_->addr_.toText();
955  addr6_length_ = addr6_.size();
956 
957  // In the following statement, the string is being read. However, the
958  // MySQL C interface does not use "const", so the "buffer" element
959  // is declared as "char*" instead of "const char*". To resolve this,
960  // the "const" is discarded. (Note that the address of addr6_.c_str()
961  // is guaranteed to be valid until the next non-const operation on
962  // addr6_.)
963  //
964  // The const_cast could be avoided by copying the string to a writable
965  // buffer and storing the address of that in the "buffer" element.
966  // However, this introduces a copy operation (with additional overhead)
967  // purely to get round the structures introduced by design of the
968  // MySQL interface (which uses the area pointed to by "buffer" as input
969  // when specifying query parameters and as output when retrieving data).
970  // For that reason, "const_cast" has been used.
971  bind_[0].buffer_type = MYSQL_TYPE_STRING;
972  bind_[0].buffer = const_cast<char*>(addr6_.c_str());
973  bind_[0].buffer_length = addr6_length_;
974  bind_[0].length = &addr6_length_;
975  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
976  // reasons, see memset() above
977 
978  // duid: varchar(128)
979  if (!lease_->duid_) {
980  isc_throw(DbOperationError, "lease6 for address " << addr6_
981  << " is missing mandatory client-id.");
982  }
983  duid_ = lease_->duid_->getDuid();
984  duid_length_ = duid_.size();
985 
986  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
987  bind_[1].buffer = reinterpret_cast<char*>(&(duid_[0]));
988  bind_[1].buffer_length = duid_length_;
989  bind_[1].length = &duid_length_;
990  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
991  // reasons, see memset() above
992 
993  // valid lifetime: unsigned int
994  bind_[2].buffer_type = MYSQL_TYPE_LONG;
995  bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
996  bind_[2].is_unsigned = MLM_TRUE;
997  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
998  // reasons, see memset() above
999 
1000  // expire: timestamp
1001  // The lease structure holds the client last transmission time (cltt_)
1002  // For convenience for external tools, this is converted to lease
1003  // expiry time (expire). The relationship is given by:
1004  //
1005  // expire = cltt_ + valid_lft_
1006  // Avoid overflow
1007  uint32_t valid_lft = lease_->valid_lft_;
1008  if (valid_lft == Lease::INFINITY_LFT) {
1009  valid_lft = 0;
1010  }
1011  MySqlConnection::convertToDatabaseTime(lease_->cltt_, valid_lft,
1012  expire_);
1013  bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
1014  bind_[3].buffer = reinterpret_cast<char*>(&expire_);
1015  bind_[3].buffer_length = sizeof(expire_);
1016  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
1017  // reasons, see memset() above
1018 
1019  // subnet_id: unsigned int
1020  // Can use lease_->subnet_id_ directly as it is of type uint32_t.
1021  bind_[4].buffer_type = MYSQL_TYPE_LONG;
1022  bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
1023  bind_[4].is_unsigned = MLM_TRUE;
1024  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
1025  // reasons, see memset() above
1026 
1027  // pref_lifetime: unsigned int
1028  // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
1029  bind_[5].buffer_type = MYSQL_TYPE_LONG;
1030  bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
1031  bind_[5].is_unsigned = MLM_TRUE;
1032  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
1033  // reasons, see memset() above
1034 
1035  // lease_type: tinyint
1036  // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
1037  lease_type_ = lease_->type_;
1038  bind_[6].buffer_type = MYSQL_TYPE_TINY;
1039  bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
1040  bind_[6].is_unsigned = MLM_TRUE;
1041  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
1042  // reasons, see memset() above
1043 
1044  // iaid: unsigned int
1045  // Can use lease_->iaid_ directly as it is of type uint32_t.
1046  bind_[7].buffer_type = MYSQL_TYPE_LONG;
1047  bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
1048  bind_[7].is_unsigned = MLM_TRUE;
1049  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1050  // reasons, see memset() above
1051 
1052  // prefix_len: unsigned tinyint
1053  // Can use lease_->prefixlen_ directly as it is uint32_t.
1054  bind_[8].buffer_type = MYSQL_TYPE_TINY;
1055  bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
1056  bind_[8].is_unsigned = MLM_TRUE;
1057  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
1058  // reasons, see memset() above
1059 
1060  // fqdn_fwd: boolean
1061  bind_[9].buffer_type = MYSQL_TYPE_TINY;
1062  bind_[9].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
1063  bind_[9].is_unsigned = MLM_TRUE;
1064  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1065  // reasons, see memset() above
1066 
1067  // fqdn_rev: boolean
1068  bind_[10].buffer_type = MYSQL_TYPE_TINY;
1069  bind_[10].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
1070  bind_[10].is_unsigned = MLM_TRUE;
1071  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
1072  // reasons, see memset() above
1073 
1074  // hostname: varchar(255)
1075  bind_[11].buffer_type = MYSQL_TYPE_STRING;
1076  bind_[11].buffer = const_cast<char*>(lease_->hostname_.c_str());
1077  bind_[11].buffer_length = lease_->hostname_.length();
1078  // bind_[11].is_null = &MLM_FALSE; // commented out for performance
1079  // reasons, see memset() above
1080 
1081  // hwaddr: varbinary(20) - hardware/MAC address
1082  HWAddrPtr hwaddr = lease_->hwaddr_;
1083  if (hwaddr) {
1084  hwaddr_ = hwaddr->hwaddr_;
1085  hwaddr_length_ = hwaddr->hwaddr_.size();
1086 
1087  // Make sure that the buffer has at least length of 1, even if
1088  // empty HW address is passed. This is required by some of the
1089  // MySQL connectors that the buffer is set to non-null value.
1090  // Otherwise, null value would be inserted into the database,
1091  // rather than empty string.
1092  if (hwaddr_.empty()) {
1093  hwaddr_.resize(1);
1094  }
1095 
1096  bind_[12].buffer_type = MYSQL_TYPE_BLOB;
1097  bind_[12].buffer = reinterpret_cast<char*>(&(hwaddr_[0]));
1098  bind_[12].buffer_length = hwaddr_length_;
1099  bind_[12].length = &hwaddr_length_;
1100  } else {
1101  bind_[12].buffer_type = MYSQL_TYPE_NULL;
1102  // According to http://dev.mysql.com/doc/refman/5.5/en/
1103  // c-api-prepared-statement-data-structures.html, the other
1104  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1105  // but let's set them to some sane values in case earlier versions
1106  // didn't have that assumption.
1107  hwaddr_null_ = MLM_TRUE;
1108  bind_[12].buffer = NULL;
1109  bind_[12].is_null = &hwaddr_null_;
1110  }
1111 
1112  // hardware type: unsigned short int (16 bits)
1113  if (hwaddr) {
1114  hwtype_ = lease->hwaddr_->htype_;
1115  bind_[13].buffer_type = MYSQL_TYPE_SHORT;
1116  bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
1117  bind_[13].is_unsigned = MLM_TRUE;
1118  } else {
1119  hwtype_ = 0;
1120  bind_[13].buffer_type = MYSQL_TYPE_NULL;
1121  // According to http://dev.mysql.com/doc/refman/5.5/en/
1122  // c-api-prepared-statement-data-structures.html, the other
1123  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1124  // but let's set them to some sane values in case earlier versions
1125  // didn't have that assumption.
1126  hwaddr_null_ = MLM_TRUE;
1127  bind_[13].buffer = NULL;
1128  bind_[13].is_null = &hwaddr_null_;
1129  }
1130 
1131  // hardware source: unsigned int (32 bits)
1132  if (hwaddr) {
1133  hwaddr_source_ = lease->hwaddr_->source_;
1134  bind_[14].buffer_type = MYSQL_TYPE_LONG;
1135  bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
1136  bind_[14].is_unsigned = MLM_TRUE;
1137  } else {
1138  hwaddr_source_ = 0;
1139  bind_[14].buffer_type = MYSQL_TYPE_NULL;
1140  // According to http://dev.mysql.com/doc/refman/5.5/en/
1141  // c-api-prepared-statement-data-structures.html, the other
1142  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1143  // but let's set them to some sane values in case earlier versions
1144  // didn't have that assumption.
1145  hwaddr_null_ = MLM_TRUE;
1146  bind_[14].buffer = NULL;
1147  bind_[14].is_null = &hwaddr_null_;
1148  }
1149 
1150  // state: uint32_t
1151  bind_[15].buffer_type = MYSQL_TYPE_LONG;
1152  bind_[15].buffer = reinterpret_cast<char*>(&lease_->state_);
1153  bind_[15].is_unsigned = MLM_TRUE;
1154  // bind_[15].is_null = &MLM_FALSE; // commented out for performance
1155  // reasons, see memset() above
1156 
1157  // user_context: text
1158  ConstElementPtr ctx = lease->getContext();
1159  if (ctx) {
1160  bind_[16].buffer_type = MYSQL_TYPE_STRING;
1161  std::string ctx_txt = ctx->str();
1162  strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
1163  bind_[16].buffer = user_context_;
1164  bind_[16].buffer_length = ctx_txt.length();
1165  // bind_[16].is_null = &MLM_FALSE; // commented out for performance
1166  // reasons, see memset() above
1167  } else {
1168  bind_[16].buffer_type = MYSQL_TYPE_NULL;
1169  }
1170 
1171  // Add the error flags
1172  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
1173 
1174  // .. and check that we have the numbers correct at compile time.
1175  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
1176 
1177  } catch (const std::exception& ex) {
1179  "Could not create bind array from Lease6: "
1180  << lease_->addr_.toText() << ", reason: " << ex.what());
1181  }
1182 
1183  // Add the data to the vector. Note the end element is one after the
1184  // end of the array.
1185  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
1186  }
1187 
1196  std::vector<MYSQL_BIND> createBindForReceive() {
1197 
1198  // Initialize MYSQL_BIND array.
1199  // It sets all fields, including is_null, to zero, so we need to set
1200  // is_null only if it should be true. This gives up minor performance
1201  // benefit while being safe approach. For improved readability, the
1202  // code that explicitly sets is_null is there, but is commented out.
1203  memset(bind_, 0, sizeof(bind_));
1204 
1205  // address: varchar(39)
1206  // A Lease6_ address has a maximum of 39 characters. The array is
1207  // one byte longer than this to guarantee that we can always null
1208  // terminate it whatever is returned.
1209  addr6_length_ = sizeof(addr6_buffer_) - 1;
1210  bind_[0].buffer_type = MYSQL_TYPE_STRING;
1211  bind_[0].buffer = addr6_buffer_;
1212  bind_[0].buffer_length = addr6_length_;
1213  bind_[0].length = &addr6_length_;
1214  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
1215  // reasons, see memset() above
1216 
1217  // client_id: varbinary(128)
1218  duid_length_ = sizeof(duid_buffer_);
1219  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
1220  bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
1221  bind_[1].buffer_length = duid_length_;
1222  bind_[1].length = &duid_length_;
1223  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
1224  // reasons, see memset() above
1225 
1226  // valid lifetime: unsigned int
1227  bind_[2].buffer_type = MYSQL_TYPE_LONG;
1228  bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
1229  bind_[2].is_unsigned = MLM_TRUE;
1230  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
1231  // reasons, see memset() above
1232 
1233  // expire: timestamp
1234  bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
1235  bind_[3].buffer = reinterpret_cast<char*>(&expire_);
1236  bind_[3].buffer_length = sizeof(expire_);
1237  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
1238  // reasons, see memset() above
1239 
1240  // subnet_id: unsigned int
1241  bind_[4].buffer_type = MYSQL_TYPE_LONG;
1242  bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
1243  bind_[4].is_unsigned = MLM_TRUE;
1244  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
1245  // reasons, see memset() above
1246 
1247  // pref_lifetime: unsigned int
1248  bind_[5].buffer_type = MYSQL_TYPE_LONG;
1249  bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
1250  bind_[5].is_unsigned = MLM_TRUE;
1251  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
1252  // reasons, see memset() above
1253 
1254  // lease_type: tinyint
1255  bind_[6].buffer_type = MYSQL_TYPE_TINY;
1256  bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
1257  bind_[6].is_unsigned = MLM_TRUE;
1258  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
1259  // reasons, see memset() above
1260 
1261  // iaid: unsigned int
1262  bind_[7].buffer_type = MYSQL_TYPE_LONG;
1263  bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
1264  bind_[7].is_unsigned = MLM_TRUE;
1265  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1266  // reasons, see memset() above
1267 
1268  // prefix_len: unsigned tinyint
1269  bind_[8].buffer_type = MYSQL_TYPE_TINY;
1270  bind_[8].buffer = reinterpret_cast<char*>(&prefix_len_);
1271  bind_[8].is_unsigned = MLM_TRUE;
1272  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
1273  // reasons, see memset() above
1274 
1275  // fqdn_fwd: boolean
1276  bind_[9].buffer_type = MYSQL_TYPE_TINY;
1277  bind_[9].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
1278  bind_[9].is_unsigned = MLM_TRUE;
1279  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
1280  // reasons, see memset() above
1281 
1282  // fqdn_rev: boolean
1283  bind_[10].buffer_type = MYSQL_TYPE_TINY;
1284  bind_[10].buffer = reinterpret_cast<char*>(&fqdn_rev_);
1285  bind_[10].is_unsigned = MLM_TRUE;
1286  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
1287  // reasons, see memset() above
1288 
1289  // hostname: varchar(255)
1290  hostname_length_ = sizeof(hostname_buffer_);
1291  bind_[11].buffer_type = MYSQL_TYPE_STRING;
1292  bind_[11].buffer = reinterpret_cast<char*>(hostname_buffer_);
1293  bind_[11].buffer_length = hostname_length_;
1294  bind_[11].length = &hostname_length_;
1295  // bind_[11].is_null = &MLM_FALSE; // commented out for performance
1296  // reasons, see memset() above
1297 
1298  // hwaddr: varbinary(20)
1299  hwaddr_null_ = MLM_FALSE;
1300  hwaddr_length_ = sizeof(hwaddr_buffer_);
1301  bind_[12].buffer_type = MYSQL_TYPE_BLOB;
1302  bind_[12].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
1303  bind_[12].buffer_length = hwaddr_length_;
1304  bind_[12].length = &hwaddr_length_;
1305  bind_[12].is_null = &hwaddr_null_;
1306 
1307  // hardware type: unsigned short int (16 bits)
1308  bind_[13].buffer_type = MYSQL_TYPE_SHORT;
1309  bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
1310  bind_[13].is_unsigned = MLM_TRUE;
1311 
1312  // hardware source: unsigned int (32 bits)
1313  bind_[14].buffer_type = MYSQL_TYPE_LONG;
1314  bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
1315  bind_[14].is_unsigned = MLM_TRUE;
1316 
1317  // state: uint32_t
1318  bind_[15].buffer_type = MYSQL_TYPE_LONG;
1319  bind_[15].buffer = reinterpret_cast<char*>(&state_);
1320  bind_[15].is_unsigned = MLM_TRUE;
1321  // bind_[15].is_null = &MLM_FALSE; // commented out for performance
1322  // reasons, see memset() above
1323 
1324  // user_context: text
1325  user_context_null_ = MLM_FALSE;
1326  user_context_length_ = sizeof(user_context_);
1327  bind_[16].buffer_type = MYSQL_TYPE_STRING;
1328  bind_[16].buffer = reinterpret_cast<char*>(user_context_);
1329  bind_[16].buffer_length = user_context_length_;
1330  bind_[16].length = &user_context_length_;
1331  bind_[16].is_null = &user_context_null_;
1332 
1333  // Add the error flags
1334  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
1335 
1336  // .. and check that we have the numbers correct at compile time.
1337  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
1338 
1339  // Add the data to the vector. Note the end element is one after the
1340  // end of the array.
1341  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
1342  }
1343 
1355  // The address buffer is declared larger than the buffer size passed
1356  // to the access function so that we can always append a null byte.
1357  // Create the IOAddress object corresponding to the received data.
1358  addr6_buffer_[addr6_length_] = '\0';
1359  std::string address = addr6_buffer_;
1360  IOAddress addr(address);
1361 
1362  // Set the lease type in a variable of the appropriate data type, which
1363  // has been initialized with an arbitrary (but valid) value.
1364  Lease::Type type = Lease::TYPE_NA;
1365  switch (lease_type_) {
1366  case Lease::TYPE_NA:
1367  type = Lease::TYPE_NA;
1368  break;
1369 
1370  case Lease::TYPE_TA:
1371  type = Lease::TYPE_TA;
1372  break;
1373 
1374  case Lease::TYPE_PD:
1375  type = Lease::TYPE_PD;
1376  break;
1377 
1378  default:
1379  isc_throw(BadValue, "invalid lease type returned (" <<
1380  static_cast<int>(lease_type_) << ") for lease with "
1381  << "address " << address << ". Only 0, 1, or 2 are "
1382  << "allowed.");
1383  }
1384 
1385  // Set up DUID,
1386  DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
1387 
1388  // Hostname is passed to Lease6 as a string object, so we have to
1389  // create it from the hostname buffer and length.
1390  std::string hostname(hostname_buffer_,
1391  hostname_buffer_ + hostname_length_);
1392 
1393  // Set hardware address if it was set
1394  HWAddrPtr hwaddr;
1395  if (hwaddr_null_ == MLM_FALSE) {
1396  hwaddr.reset(new HWAddr(hwaddr_buffer_, hwaddr_length_, hwtype_));
1397  hwaddr->source_ = hwaddr_source_;
1398  }
1399 
1400  // Convert user_context to string as well.
1401  std::string user_context;
1402  if (user_context_null_ == MLM_FALSE) {
1403  user_context_[user_context_length_] = '\0';
1404  user_context.assign(user_context_);
1405  }
1406 
1407  // Set the user context if there is one.
1408  ConstElementPtr ctx;
1409  if (!user_context.empty()) {
1410  ctx = Element::fromJSON(user_context);
1411  if (!ctx || (ctx->getType() != Element::map)) {
1412  isc_throw(BadValue, "user context '" << user_context
1413  << "' is not a JSON map");
1414  }
1415  }
1416 
1417  // Create the lease and set the cltt (after converting from the
1418  // expire time retrieved from the database).
1419  Lease6Ptr result(boost::make_shared<Lease6>(type, addr, duid_ptr, iaid_,
1420  pref_lifetime_,
1421  valid_lifetime_, subnet_id_,
1422  fqdn_fwd_, fqdn_rev_,
1423  hostname, hwaddr,
1424  prefix_len_));
1425  time_t cltt = 0;
1426  // Recover from overflow
1427  uint32_t valid_lft = valid_lifetime_;
1428  if (valid_lft == Lease::INFINITY_LFT) {
1429  valid_lft = 0;
1430  }
1431  MySqlConnection::convertFromDatabaseTime(expire_, valid_lft, cltt);
1432  // Update cltt_ and current_cltt_ explicitly.
1433  result->cltt_ = cltt;
1434  result->current_cltt_ = cltt;
1435 
1436  // Set state.
1437  result->state_ = state_;
1438 
1439  if (ctx) {
1440  result->setContext(ctx);
1441  }
1442 
1443  return (result);
1444  }
1445 
1456  std::string getErrorColumns() {
1457  return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
1458  }
1459 
1460 private:
1461 
1462  // Note: All array lengths are equal to the corresponding variable in the
1463  // schema.
1464  // Note: arrays are declared fixed length for speed of creation
1465  std::string addr6_;
1466  char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];
1467  unsigned long addr6_length_;
1468  MYSQL_BIND bind_[LEASE_COLUMNS];
1469  std::string columns_[LEASE_COLUMNS];
1470  my_bool error_[LEASE_COLUMNS];
1471  Lease6Ptr lease_;
1472  std::vector<uint8_t> hwaddr_;
1473  uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
1474  unsigned long hwaddr_length_;
1475  my_bool hwaddr_null_;
1476  std::vector<uint8_t> duid_;
1477  uint8_t duid_buffer_[DUID::MAX_DUID_LEN];
1478  unsigned long duid_length_;
1479  MYSQL_TIME expire_;
1480  uint32_t iaid_;
1481  uint8_t lease_type_;
1482  uint8_t prefix_len_;
1483  uint32_t pref_lifetime_;
1484  uint32_t subnet_id_;
1485  uint32_t valid_lifetime_;
1486  my_bool fqdn_fwd_;
1487  my_bool fqdn_rev_;
1488  char hostname_buffer_[HOSTNAME_MAX_LEN];
1489  unsigned long hostname_length_;
1490  uint16_t hwtype_;
1491  uint32_t hwaddr_source_;
1492  uint32_t state_;
1493  char user_context_[USER_CONTEXT_MAX_LEN];
1494  unsigned long user_context_length_;
1495  my_bool user_context_null_;
1496 };
1497 
1504 
1506 public:
1507 
1516  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1517  const bool fetch_type)
1518  : conn_(conn), statement_index_(statement_index), statement_(NULL),
1519  fetch_type_(fetch_type),
1520  // Set the number of columns in the bind array based on fetch_type
1521  // This is the number of columns expected in the result set
1522  bind_(fetch_type_ ? 4 : 3),
1523  subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
1524  validateStatement();
1525  }
1526 
1536  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1537  const bool fetch_type, const SubnetID& subnet_id)
1538  : LeaseStatsQuery(subnet_id), conn_(conn), statement_index_(statement_index),
1539  statement_(NULL), fetch_type_(fetch_type),
1540  // Set the number of columns in the bind array based on fetch_type
1541  // This is the number of columns expected in the result set
1542  bind_(fetch_type_ ? 4 : 3),
1543  subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
1544  validateStatement();
1545  }
1546 
1559  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1560  const bool fetch_type, const SubnetID& first_subnet_id,
1561  const SubnetID& last_subnet_id)
1562  : LeaseStatsQuery(first_subnet_id, last_subnet_id), conn_(conn),
1563  statement_index_(statement_index), statement_(NULL), fetch_type_(fetch_type),
1564  // Set the number of columns in the bind array based on fetch_type
1565  // This is the number of columns expected in the result set
1566  bind_(fetch_type_ ? 4 : 3),
1567  subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
1568  validateStatement();
1569  }
1570 
1573  (void) mysql_stmt_free_result(statement_);
1574  }
1575 
1583  void start() {
1584  // Set up where clause inputs if needed.
1585  if (getSelectMode() != ALL_SUBNETS) {
1586  MYSQL_BIND inbind[2];
1587  memset(inbind, 0, sizeof(inbind));
1588 
1589  // Add first_subnet_id used by both single and range.
1590  inbind[0].buffer_type = MYSQL_TYPE_LONG;
1591  inbind[0].buffer = reinterpret_cast<char*>(&first_subnet_id_);
1592  inbind[0].is_unsigned = MLM_TRUE;
1593 
1594  // Add last_subnet_id for range.
1595  if (getSelectMode() == SUBNET_RANGE) {
1596  inbind[1].buffer_type = MYSQL_TYPE_LONG;
1597  inbind[1].buffer = reinterpret_cast<char*>(&last_subnet_id_);
1598  inbind[1].is_unsigned = MLM_TRUE;
1599  }
1600 
1601  // Bind the parameters to the statement
1602  int status = mysql_stmt_bind_param(statement_, &inbind[0]);
1603  conn_.checkError(status, statement_index_, "unable to bind parameters");
1604  }
1605 
1606  int col = 0;
1607  // subnet_id: unsigned int
1608  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1609  bind_[col].buffer = reinterpret_cast<char*>(&subnet_id_);
1610  bind_[col].is_unsigned = MLM_TRUE;
1611  ++col;
1612 
1613  // Fetch the lease type if we were told to do so.
1614  if (fetch_type_) {
1615  // lease type: uint32_t
1616  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1617  bind_[col].buffer = reinterpret_cast<char*>(&lease_type_);
1618  bind_[col].is_unsigned = MLM_TRUE;
1619  ++col;
1620  } else {
1621  fetch_type_ = Lease::TYPE_NA;
1622  }
1623 
1624  // state: uint32_t
1625  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1626  bind_[col].buffer = reinterpret_cast<char*>(&state_);
1627  bind_[col].is_unsigned = MLM_TRUE;
1628  ++col;
1629 
1630  // state_count_: int64_t
1631  bind_[col].buffer_type = MYSQL_TYPE_LONGLONG;
1632  bind_[col].buffer = reinterpret_cast<char*>(&state_count_);
1633  //bind_[col].is_unsigned = MLM_FALSE;
1634 
1635  // Set up the MYSQL_BIND array for the data being returned
1636  // and bind it to the statement.
1637  int status = mysql_stmt_bind_result(statement_, &bind_[0]);
1638  conn_.checkError(status, statement_index_, "outbound binding failed");
1639 
1640  // Execute the statement
1641  status = MysqlExecuteStatement(statement_);
1642  conn_.checkError(status, statement_index_, "unable to execute");
1643 
1644  // Ensure that all the lease information is retrieved in one go to avoid
1645  // overhead of going back and forth between client and server.
1646  status = mysql_stmt_store_result(statement_);
1647  conn_.checkError(status, statement_index_, "results storage failed");
1648  }
1649 
1666  bool have_row = false;
1667  int status = mysql_stmt_fetch(statement_);
1668  if (status == MLM_MYSQL_FETCH_SUCCESS) {
1669  row.subnet_id_ = static_cast<SubnetID>(subnet_id_);
1670  row.lease_type_ = static_cast<Lease::Type>(lease_type_);
1671  row.lease_state_ = state_;
1672  if (state_count_ >= 0) {
1673  row.state_count_ = state_count_;
1674  } else {
1675  row.state_count_ = 0;
1676  if (!negative_count_) {
1677  negative_count_ = true;
1679  }
1680  }
1681  have_row = true;
1682  } else if (status != MYSQL_NO_DATA) {
1683  conn_.checkError(status, statement_index_, "getNextRow failed");
1684  }
1685 
1686  return (have_row);
1687  }
1688 
1689 private:
1690 
1694  void validateStatement() {
1695  if (statement_index_ >= MySqlLeaseMgr::NUM_STATEMENTS) {
1696  isc_throw(BadValue, "MySqlLeaseStatsQuery"
1697  " - invalid statement index" << statement_index_);
1698  }
1699 
1700  statement_ = conn_.statements_[statement_index_];
1701  }
1702 
1704  MySqlConnection& conn_;
1705 
1707  size_t statement_index_;
1708 
1710  MYSQL_STMT *statement_;
1711 
1713  bool fetch_type_;
1714 
1716  std::vector<MYSQL_BIND> bind_;
1717 
1719  uint32_t subnet_id_;
1720 
1722  uint32_t lease_type_;
1723 
1725  uint32_t state_;
1726 
1728  int64_t state_count_;
1729 
1731  static bool negative_count_;
1732 };
1733 
1734 // Initialize negative state count flag to false.
1735 bool MySqlLeaseStatsQuery::negative_count_ = false;
1736 
1737 // MySqlLeaseContext Constructor
1738 
1739 MySqlLeaseContext::MySqlLeaseContext(const DatabaseConnection::ParameterMap& parameters,
1740  IOServiceAccessorPtr io_service_accessor,
1741  DbCallback db_reconnect_callback)
1742  : conn_(parameters, io_service_accessor, db_reconnect_callback) {
1743 }
1744 
1745 // MySqlLeaseContextAlloc Constructor and Destructor
1746 
1747 MySqlLeaseMgr::MySqlLeaseContextAlloc::MySqlLeaseContextAlloc(
1748  const MySqlLeaseMgr& mgr) : ctx_(), mgr_(mgr) {
1749 
1750  if (MultiThreadingMgr::instance().getMode()) {
1751  // multi-threaded
1752  {
1753  // we need to protect the whole pool_ operation, hence extra scope {}
1754  lock_guard<mutex> lock(mgr_.pool_->mutex_);
1755  if (!mgr_.pool_->pool_.empty()) {
1756  ctx_ = mgr_.pool_->pool_.back();
1757  mgr_.pool_->pool_.pop_back();
1758  }
1759  }
1760  if (!ctx_) {
1761  ctx_ = mgr_.createContext();
1762  }
1763  } else {
1764  // single-threaded
1765  if (mgr_.pool_->pool_.empty()) {
1766  isc_throw(Unexpected, "No available MySQL lease context?!");
1767  }
1768  ctx_ = mgr_.pool_->pool_.back();
1769  }
1770 }
1771 
1772 MySqlLeaseMgr::MySqlLeaseContextAlloc::~MySqlLeaseContextAlloc() {
1773  if (MultiThreadingMgr::instance().getMode()) {
1774  // multi-threaded
1775  lock_guard<mutex> lock(mgr_.pool_->mutex_);
1776  mgr_.pool_->pool_.push_back(ctx_);
1777  }
1778  // If running in single-threaded mode, there's nothing to do here.
1779 }
1780 
1781 // MySqlLeaseMgr Constructor and Destructor
1782 
1784  : parameters_(parameters), timer_name_("") {
1785 
1786  // Create unique timer name per instance.
1787  timer_name_ = "MySqlLeaseMgr[";
1788  timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
1789  timer_name_ += "]DbReconnectTimer";
1790 
1791  // Validate schema version first.
1792  std::pair<uint32_t, uint32_t> code_version(MYSQL_SCHEMA_VERSION_MAJOR,
1794  std::pair<uint32_t, uint32_t> db_version = getVersion();
1795  if (code_version != db_version) {
1797  "MySQL schema version mismatch: need version: "
1798  << code_version.first << "." << code_version.second
1799  << " found version: " << db_version.first << "."
1800  << db_version.second);
1801  }
1802 
1803  // Create an initial context.
1804  pool_.reset(new MySqlLeaseContextPool());
1805  pool_->pool_.push_back(createContext());
1806 }
1807 
1809 }
1810 
1811 bool
1814 
1815  // Invoke application layer connection lost callback.
1816  if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
1817  return (false);
1818  }
1819 
1820  bool reopened = false;
1821 
1822  const std::string timer_name = db_reconnect_ctl->timerName();
1823 
1824  // At least one connection was lost.
1825  try {
1826  CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
1828  LeaseMgrFactory::create(cfg_db->getLeaseDbAccessString());
1829  reopened = true;
1830  } catch (const std::exception& ex) {
1832  .arg(ex.what());
1833  }
1834 
1835  if (reopened) {
1836  // Cancel the timer.
1837  if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
1838  TimerMgr::instance()->unregisterTimer(timer_name);
1839  }
1840 
1841  // Invoke application layer connection recovered callback.
1842  if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
1843  return (false);
1844  }
1845  } else {
1846  if (!db_reconnect_ctl->checkRetries()) {
1847  // We're out of retries, log it and initiate shutdown.
1849  .arg(db_reconnect_ctl->maxRetries());
1850 
1851  // Cancel the timer.
1852  if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
1853  TimerMgr::instance()->unregisterTimer(timer_name);
1854  }
1855 
1856  // Invoke application layer connection failed callback.
1858  return (false);
1859  }
1860 
1862  .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
1863  .arg(db_reconnect_ctl->maxRetries())
1864  .arg(db_reconnect_ctl->retryInterval());
1865 
1866  // Start the timer.
1867  if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
1868  TimerMgr::instance()->registerTimer(timer_name,
1869  std::bind(&MySqlLeaseMgr::dbReconnect, db_reconnect_ctl),
1870  db_reconnect_ctl->retryInterval(),
1872  }
1873  TimerMgr::instance()->setup(timer_name);
1874  }
1875 
1876  return (true);
1877 }
1878 
1879 // Create context.
1880 
1883  MySqlLeaseContextPtr ctx(new MySqlLeaseContext(parameters_,
1886 
1887  // Open the database.
1888  ctx->conn_.openDatabase();
1889 
1890  // Prepare all statements likely to be used.
1891  ctx->conn_.prepareStatements(tagged_statements.begin(),
1892  tagged_statements.end());
1893 
1894  // Create the exchange objects for use in exchanging data between the
1895  // program and the database.
1896  ctx->exchange4_.reset(new MySqlLease4Exchange());
1897  ctx->exchange6_.reset(new MySqlLease6Exchange());
1898 
1899  // Create ReconnectCtl for this connection.
1900  ctx->conn_.makeReconnectCtl(timer_name_);
1901 
1902  return (ctx);
1903 }
1904 
1905 std::string
1907  std::stringstream tmp;
1908  tmp << "MySQL backend " << MYSQL_SCHEMA_VERSION_MAJOR;
1909  tmp << "." << MYSQL_SCHEMA_VERSION_MINOR;
1910  tmp << ", library " << mysql_get_client_info();
1911  return (tmp.str());
1912 }
1913 
1914 // Add leases to the database. The two public methods accept a lease object
1915 // (either V4 of V6), bind the contents to the appropriate prepared
1916 // statement, then call common code to execute the statement.
1917 
1918 bool
1919 MySqlLeaseMgr::addLeaseCommon(MySqlLeaseContextPtr& ctx,
1920  StatementIndex stindex,
1921  std::vector<MYSQL_BIND>& bind) {
1922 
1923  // Bind the parameters to the statement
1924  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], &bind[0]);
1925  checkError(ctx, status, stindex, "unable to bind parameters");
1926 
1927  // Execute the statement
1928  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
1929  if (status != 0) {
1930 
1931  // Failure: check for the special case of duplicate entry. If this is
1932  // the case, we return false to indicate that the row was not added.
1933  // Otherwise we throw an exception.
1934  if (mysql_errno(ctx->conn_.mysql_) == ER_DUP_ENTRY) {
1935  return (false);
1936  }
1937  checkError(ctx, status, stindex, "unable to execute");
1938  }
1939 
1940  // Insert succeeded
1941  return (true);
1942 }
1943 
1944 bool
1947  .arg(lease->addr_.toText());
1948 
1949  // Get a context
1950  MySqlLeaseContextAlloc get_context(*this);
1951  MySqlLeaseContextPtr ctx = get_context.ctx_;
1952 
1953  // Create the MYSQL_BIND array for the lease
1954  std::vector<MYSQL_BIND> bind = ctx->exchange4_->createBindForSend(lease);
1955 
1956  // ... and drop to common code.
1957  auto result = addLeaseCommon(ctx, INSERT_LEASE4, bind);
1958 
1959  // Update lease current expiration time (allows update between the creation
1960  // of the Lease up to the point of insertion in the database).
1961  lease->updateCurrentExpirationTime();
1962 
1963  return (result);
1964 }
1965 
1966 bool
1969  .arg(lease->addr_.toText())
1970  .arg(lease->type_);
1971 
1972  // Get a context
1973  MySqlLeaseContextAlloc get_context(*this);
1974  MySqlLeaseContextPtr ctx = get_context.ctx_;
1975 
1976  // Create the MYSQL_BIND array for the lease
1977  std::vector<MYSQL_BIND> bind = ctx->exchange6_->createBindForSend(lease);
1978 
1979  // ... and drop to common code.
1980  auto result = addLeaseCommon(ctx, INSERT_LEASE6, bind);
1981 
1982  // Update lease current expiration time (allows update between the creation
1983  // of the Lease up to the point of insertion in the database).
1984  lease->updateCurrentExpirationTime();
1985 
1986  return (result);
1987 }
1988 
1989 // Extraction of leases from the database.
1990 //
1991 // All getLease() methods ultimately call getLeaseCollection(). This
1992 // binds the input parameters passed to it with the appropriate prepared
1993 // statement and executes the statement. It then gets the results from the
1994 // database. getlease() methods that expect a single result back call it
1995 // with the "single" parameter set true: this causes an exception to be
1996 // generated if multiple records can be retrieved from the result set. (Such
1997 // an occurrence either indicates corruption in the database, or that an
1998 // assumption that a query can only return a single record is incorrect.)
1999 // Methods that require a collection of records have "single" set to the
2000 // default value of false. The logic is the same for both Lease4 and Lease6
2001 // objects, so the code is templated.
2002 //
2003 // Methods that require a collection of objects access this method through
2004 // two interface methods (also called getLeaseCollection()). These are
2005 // short enough as to be defined in the header file: all they do is to supply
2006 // the appropriate MySqlLeaseXExchange object depending on the type of the
2007 // LeaseCollection objects passed to them.
2008 //
2009 // Methods that require a single object to be returned access the method
2010 // through two interface methods (called getLease()). As well as supplying
2011 // the appropriate exchange object, they convert between lease collection
2012 // holding zero or one leases into an appropriate Lease object.
2013 
2014 template <typename Exchange, typename LeaseCollection>
2015 void
2016 MySqlLeaseMgr::getLeaseCollection(MySqlLeaseContextPtr& ctx,
2017  StatementIndex stindex,
2018  MYSQL_BIND* bind,
2019  Exchange& exchange,
2020  LeaseCollection& result,
2021  bool single) const {
2022  int status;
2023 
2024  if (bind) {
2025  // Bind the selection parameters to the statement
2026  status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], bind);
2027  checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
2028  }
2029 
2030  // Set up the MYSQL_BIND array for the data being returned and bind it to
2031  // the statement.
2032  std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
2033  status = mysql_stmt_bind_result(ctx->conn_.statements_[stindex], &outbind[0]);
2034  checkError(ctx, status, stindex, "unable to bind SELECT clause parameters");
2035 
2036  // Execute the statement
2037  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2038  checkError(ctx, status, stindex, "unable to execute");
2039 
2040  // Ensure that all the lease information is retrieved in one go to avoid
2041  // overhead of going back and forth between client and server.
2042  status = mysql_stmt_store_result(ctx->conn_.statements_[stindex]);
2043  checkError(ctx, status, stindex, "unable to set up for storing all results");
2044 
2045  // Set up the fetch "release" object to release resources associated
2046  // with the call to mysql_stmt_fetch when this method exits, then
2047  // retrieve the data.
2048  MySqlFreeResult fetch_release(ctx->conn_.statements_[stindex]);
2049  int count = 0;
2050  while ((status = mysql_stmt_fetch(ctx->conn_.statements_[stindex])) == 0) {
2051  try {
2052  result.push_back(exchange->getLeaseData());
2053 
2054  } catch (const isc::BadValue& ex) {
2055  // Rethrow the exception with a bit more data.
2056  isc_throw(BadValue, ex.what() << ". Statement is <" <<
2057  ctx->conn_.text_statements_[stindex] << ">");
2058  }
2059 
2060  if (single && (++count > 1)) {
2061  isc_throw(MultipleRecords, "multiple records were found in the "
2062  "database where only one was expected for query "
2063  << ctx->conn_.text_statements_[stindex]);
2064  }
2065  }
2066 
2067  // How did the fetch end?
2068  if (status == 1) {
2069  // Error - unable to fetch results
2070  checkError(ctx, status, stindex, "unable to fetch results");
2071  } else if (status == MYSQL_DATA_TRUNCATED) {
2072  // Data truncated - throw an exception indicating what was at fault
2073  isc_throw(DataTruncated, ctx->conn_.text_statements_[stindex]
2074  << " returned truncated data: columns affected are "
2075  << exchange->getErrorColumns());
2076  }
2077 }
2078 
2079 void
2080 MySqlLeaseMgr::getLease(MySqlLeaseContextPtr& ctx,
2081  StatementIndex stindex, MYSQL_BIND* bind,
2082  Lease4Ptr& result) const {
2083  // Create appropriate collection object and get all leases matching
2084  // the selection criteria. The "single" parameter is true to indicate
2085  // that the called method should throw an exception if multiple
2086  // matching records are found: this particular method is called when only
2087  // one or zero matches is expected.
2088  Lease4Collection collection;
2089  getLeaseCollection(ctx, stindex, bind, ctx->exchange4_, collection, true);
2090 
2091  // Return single record if present, else clear the lease.
2092  if (collection.empty()) {
2093  result.reset();
2094  } else {
2095  result = *collection.begin();
2096  }
2097 }
2098 
2099 void
2100 MySqlLeaseMgr::getLease(MySqlLeaseContextPtr& ctx,
2101  StatementIndex stindex, MYSQL_BIND* bind,
2102  Lease6Ptr& result) const {
2103  // Create appropriate collection object and get all leases matching
2104  // the selection criteria. The "single" parameter is true to indicate
2105  // that the called method should throw an exception if multiple
2106  // matching records are found: this particular method is called when only
2107  // one or zero matches is expected.
2108  Lease6Collection collection;
2109  getLeaseCollection(ctx, stindex, bind, ctx->exchange6_, collection, true);
2110 
2111  // Return single record if present, else clear the lease.
2112  if (collection.empty()) {
2113  result.reset();
2114  } else {
2115  result = *collection.begin();
2116  }
2117 }
2118 
2119 // Basic lease access methods. Obtain leases from the database using various
2120 // criteria.
2121 
2122 Lease4Ptr
2125  .arg(addr.toText());
2126 
2127  // Set up the WHERE clause value
2128  MYSQL_BIND inbind[1];
2129  memset(inbind, 0, sizeof(inbind));
2130 
2131  uint32_t addr4 = addr.toUint32();
2132  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2133  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
2134  inbind[0].is_unsigned = MLM_TRUE;
2135 
2136  // Get the data
2137  Lease4Ptr result;
2138 
2139  // Get a context
2140  MySqlLeaseContextAlloc get_context(*this);
2141  MySqlLeaseContextPtr ctx = get_context.ctx_;
2142 
2143  getLease(ctx, GET_LEASE4_ADDR, inbind, result);
2144 
2145  return (result);
2146 }
2147 
2149 MySqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
2151  .arg(hwaddr.toText());
2152 
2153  // Set up the WHERE clause value
2154  MYSQL_BIND inbind[1];
2155  memset(inbind, 0, sizeof(inbind));
2156 
2157  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2158 
2159  unsigned long hwaddr_length = hwaddr.hwaddr_.size();
2160 
2161  // If the data happens to be empty, we have to create a 1 byte dummy
2162  // buffer and pass it to the binding.
2163  uint8_t single_byte_data = 0;
2164 
2165  // As "buffer" is "char*" - even though the data is being read - we need
2166  // to cast away the "const"ness as well as reinterpreting the data as
2167  // a "char*". (We could avoid the "const_cast" by copying the data to a
2168  // local variable, but as the data is only being read, this introduces
2169  // an unnecessary copy).
2170  uint8_t* data = !hwaddr.hwaddr_.empty() ? const_cast<uint8_t*>(&hwaddr.hwaddr_[0])
2171  : &single_byte_data;
2172 
2173  inbind[0].buffer = reinterpret_cast<char*>(data);
2174  inbind[0].buffer_length = hwaddr_length;
2175  inbind[0].length = &hwaddr_length;
2176 
2177  // Get the data
2178  Lease4Collection result;
2179 
2180  // Get a context
2181  MySqlLeaseContextAlloc get_context(*this);
2182  MySqlLeaseContextPtr ctx = get_context.ctx_;
2183 
2184  getLeaseCollection(ctx, GET_LEASE4_HWADDR, inbind, result);
2185 
2186  return (result);
2187 }
2188 
2189 Lease4Ptr
2190 MySqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
2192  .arg(subnet_id)
2193  .arg(hwaddr.toText());
2194 
2195  // Set up the WHERE clause value
2196  MYSQL_BIND inbind[2];
2197  memset(inbind, 0, sizeof(inbind));
2198 
2199  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2200 
2201  unsigned long hwaddr_length = hwaddr.hwaddr_.size();
2202 
2203  // If the data happens to be empty, we have to create a 1 byte dummy
2204  // buffer and pass it to the binding.
2205  std::vector<uint8_t> single_byte_vec(1);
2206 
2207  // As "buffer" is "char*" - even though the data is being read - we need
2208  // to cast away the "const"ness as well as reinterpreting the data as
2209  // a "char*". (We could avoid the "const_cast" by copying the data to a
2210  // local variable, but as the data is only being read, this introduces
2211  // an unnecessary copy).
2212  uint8_t* data = !hwaddr.hwaddr_.empty() ? const_cast<uint8_t*>(&hwaddr.hwaddr_[0])
2213  : &single_byte_vec[0];
2214 
2215  inbind[0].buffer = reinterpret_cast<char*>(data);
2216  inbind[0].buffer_length = hwaddr_length;
2217  inbind[0].length = &hwaddr_length;
2218 
2219  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2220  inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
2221  inbind[1].is_unsigned = MLM_TRUE;
2222 
2223  // Get the data
2224  Lease4Ptr result;
2225 
2226  // Get a context
2227  MySqlLeaseContextAlloc get_context(*this);
2228  MySqlLeaseContextPtr ctx = get_context.ctx_;
2229 
2230  getLease(ctx, GET_LEASE4_HWADDR_SUBID, inbind, result);
2231 
2232  return (result);
2233 }
2234 
2236 MySqlLeaseMgr::getLease4(const ClientId& clientid) const {
2238  .arg(clientid.toText());
2239 
2240  // Set up the WHERE clause value
2241  MYSQL_BIND inbind[1];
2242  memset(inbind, 0, sizeof(inbind));
2243 
2244  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2245 
2246  std::vector<uint8_t> client_data = clientid.getClientId();
2247  unsigned long client_data_length = client_data.size();
2248 
2249  // If the data happens to be empty, we have to create a 1 byte dummy
2250  // buffer and pass it to the binding.
2251  if (client_data.empty()) {
2252  client_data.resize(1);
2253  }
2254 
2255  inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
2256  inbind[0].buffer_length = client_data_length;
2257  inbind[0].length = &client_data_length;
2258 
2259  // Get the data
2260  Lease4Collection result;
2261 
2262  // Get a context
2263  MySqlLeaseContextAlloc get_context(*this);
2264  MySqlLeaseContextPtr ctx = get_context.ctx_;
2265 
2266  getLeaseCollection(ctx, GET_LEASE4_CLIENTID, inbind, result);
2267 
2268  return (result);
2269 }
2270 
2271 Lease4Ptr
2272 MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
2274  .arg(subnet_id)
2275  .arg(clientid.toText());
2276 
2277  // Set up the WHERE clause value
2278  MYSQL_BIND inbind[2];
2279  memset(inbind, 0, sizeof(inbind));
2280 
2281  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2282 
2283  std::vector<uint8_t> client_data = clientid.getClientId();
2284  unsigned long client_data_length = client_data.size();
2285 
2286  // If the data happens to be empty, we have to create a 1 byte dummy
2287  // buffer and pass it to the binding.
2288  if (client_data.empty()) {
2289  client_data.resize(1);
2290  }
2291 
2292  inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
2293  inbind[0].buffer_length = client_data_length;
2294  inbind[0].length = &client_data_length;
2295 
2296  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2297  inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
2298  inbind[1].is_unsigned = MLM_TRUE;
2299 
2300  // Get the data
2301  Lease4Ptr result;
2302 
2303  // Get a context
2304  MySqlLeaseContextAlloc get_context(*this);
2305  MySqlLeaseContextPtr ctx = get_context.ctx_;
2306 
2307  getLease(ctx, GET_LEASE4_CLIENTID_SUBID, inbind, result);
2308 
2309  return (result);
2310 }
2311 
2315  .arg(subnet_id);
2316 
2317  // Set up the WHERE clause value
2318  MYSQL_BIND inbind[1];
2319  memset(inbind, 0, sizeof(inbind));
2320 
2321  // Subnet ID
2322  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2323  inbind[0].buffer = reinterpret_cast<char*>(&subnet_id);
2324  inbind[0].is_unsigned = MLM_TRUE;
2325 
2326  // ... and get the data
2327  Lease4Collection result;
2328 
2329  // Get a context
2330  MySqlLeaseContextAlloc get_context(*this);
2331  MySqlLeaseContextPtr ctx = get_context.ctx_;
2332 
2333  getLeaseCollection(ctx, GET_LEASE4_SUBID, inbind, result);
2334 
2335  return (result);
2336 }
2337 
2339 MySqlLeaseMgr::getLeases4(const std::string& hostname) const {
2341  .arg(hostname);
2342 
2343  // Set up the WHERE clause value
2344  MYSQL_BIND inbind[1];
2345  memset(inbind, 0, sizeof(inbind));
2346 
2347  // Hostname
2348  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2349  inbind[0].buffer = const_cast<char*>(hostname.c_str());
2350  inbind[0].buffer_length = hostname.length();
2351 
2352  // ... and get the data
2353  Lease4Collection result;
2354 
2355  // Get a context
2356  MySqlLeaseContextAlloc get_context(*this);
2357  MySqlLeaseContextPtr ctx = get_context.ctx_;
2358 
2359  getLeaseCollection(ctx, GET_LEASE4_HOSTNAME, inbind, result);
2360 
2361  return (result);
2362 }
2363 
2367 
2368  Lease4Collection result;
2369 
2370  // Get a context
2371  MySqlLeaseContextAlloc get_context(*this);
2372  MySqlLeaseContextPtr ctx = get_context.ctx_;
2373 
2374  getLeaseCollection(ctx, GET_LEASE4, 0, result);
2375 
2376  return (result);
2377 }
2378 
2380 MySqlLeaseMgr::getLeases4(const IOAddress& lower_bound_address,
2381  const LeasePageSize& page_size) const {
2382  // Expecting IPv4 address.
2383  if (!lower_bound_address.isV4()) {
2384  isc_throw(InvalidAddressFamily, "expected IPv4 address while "
2385  "retrieving leases from the lease database, got "
2386  << lower_bound_address);
2387  }
2388 
2390  .arg(page_size.page_size_)
2391  .arg(lower_bound_address.toText());
2392 
2393  // Prepare WHERE clause
2394  MYSQL_BIND inbind[2];
2395  memset(inbind, 0, sizeof(inbind));
2396 
2397  // Bind lower bound address
2398  uint32_t lb_address_data = lower_bound_address.toUint32();
2399  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2400  inbind[0].buffer = reinterpret_cast<char*>(&lb_address_data);
2401  inbind[0].is_unsigned = MLM_TRUE;
2402 
2403  // Bind page size value
2404  size_t* ps = const_cast<size_t*>(&page_size.page_size_);
2405  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2406  inbind[1].buffer = reinterpret_cast<char*>(ps);
2407  inbind[1].is_unsigned = MLM_TRUE;
2408 
2409  // Get the leases
2410  Lease4Collection result;
2411 
2412  // Get a context
2413  MySqlLeaseContextAlloc get_context(*this);
2414  MySqlLeaseContextPtr ctx = get_context.ctx_;
2415 
2416  getLeaseCollection(ctx, GET_LEASE4_PAGE, inbind, result);
2417 
2418  return (result);
2419 }
2420 
2421 Lease6Ptr
2423  const IOAddress& addr) const {
2425  .arg(addr.toText())
2426  .arg(lease_type);
2427 
2428  // Set up the WHERE clause value
2429  MYSQL_BIND inbind[2];
2430  memset(inbind, 0, sizeof(inbind));
2431 
2432  std::string addr6 = addr.toText();
2433  unsigned long addr6_length = addr6.size();
2434 
2435  // See the earlier description of the use of "const_cast" when accessing
2436  // the address for an explanation of the reason.
2437  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2438  inbind[0].buffer = const_cast<char*>(addr6.c_str());
2439  inbind[0].buffer_length = addr6_length;
2440  inbind[0].length = &addr6_length;
2441 
2442  // LEASE_TYPE
2443  inbind[1].buffer_type = MYSQL_TYPE_TINY;
2444  inbind[1].buffer = reinterpret_cast<char*>(&lease_type);
2445  inbind[1].is_unsigned = MLM_TRUE;
2446 
2447  Lease6Ptr result;
2448 
2449  // Get a context
2450  MySqlLeaseContextAlloc get_context(*this);
2451  MySqlLeaseContextPtr ctx = get_context.ctx_;
2452 
2453  getLease(ctx, GET_LEASE6_ADDR, inbind, result);
2454 
2455  return (result);
2456 }
2457 
2460  uint32_t iaid) const {
2462  .arg(iaid)
2463  .arg(duid.toText())
2464  .arg(lease_type);
2465 
2466  // Set up the WHERE clause value
2467  MYSQL_BIND inbind[3];
2468  memset(inbind, 0, sizeof(inbind));
2469 
2470  // In the following statement, the DUID is being read. However, the
2471  // MySQL C interface does not use "const", so the "buffer" element
2472  // is declared as "char*" instead of "const char*". To resolve this,
2473  // the "const" is discarded before the uint8_t* is cast to char*.
2474  //
2475  // Note that the const_cast could be avoided by copying the DUID to
2476  // a writable buffer and storing the address of that in the "buffer"
2477  // element. However, this introduces a copy operation (with additional
2478  // overhead) purely to get round the structures introduced by design of
2479  // the MySQL interface (which uses the area pointed to by "buffer" as
2480  // input when specifying query parameters and as output when retrieving
2481  // data). For that reason, "const_cast" has been used.
2482  const vector<uint8_t>& duid_vector = duid.getDuid();
2483  unsigned long duid_length = duid_vector.size();
2484 
2485  // Make sure that the buffer has at least length of 1, even if
2486  // empty client id is passed. This is required by some of the
2487  // MySQL connectors that the buffer is set to non-null value.
2488  // Otherwise, null value would be inserted into the database,
2489  // rather than empty string.
2490  uint8_t single_byte_data = 0;
2491  uint8_t* data = !duid_vector.empty() ? const_cast<uint8_t*>(&duid_vector[0])
2492  : &single_byte_data;
2493 
2494  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2495  inbind[0].buffer = reinterpret_cast<char*>(data);
2496  inbind[0].buffer_length = duid_length;
2497  inbind[0].length = &duid_length;
2498 
2499  // IAID
2500  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2501  inbind[1].buffer = reinterpret_cast<char*>(&iaid);
2502  inbind[1].is_unsigned = MLM_TRUE;
2503 
2504  // LEASE_TYPE
2505  inbind[2].buffer_type = MYSQL_TYPE_TINY;
2506  inbind[2].buffer = reinterpret_cast<char*>(&lease_type);
2507  inbind[2].is_unsigned = MLM_TRUE;
2508 
2509  // ... and get the data
2510  Lease6Collection result;
2511 
2512  // Get a context
2513  MySqlLeaseContextAlloc get_context(*this);
2514  MySqlLeaseContextPtr ctx = get_context.ctx_;
2515 
2516  getLeaseCollection(ctx, GET_LEASE6_DUID_IAID, inbind, result);
2517 
2518  return (result);
2519 }
2520 
2523  uint32_t iaid, SubnetID subnet_id) const {
2525  .arg(iaid)
2526  .arg(subnet_id)
2527  .arg(duid.toText())
2528  .arg(lease_type);
2529 
2530  // Set up the WHERE clause value
2531  MYSQL_BIND inbind[4];
2532  memset(inbind, 0, sizeof(inbind));
2533 
2534  // See the earlier description of the use of "const_cast" when accessing
2535  // the DUID for an explanation of the reason.
2536  const vector<uint8_t>& duid_vector = duid.getDuid();
2537  unsigned long duid_length = duid_vector.size();
2538  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2539  inbind[0].buffer = reinterpret_cast<char*>(
2540  const_cast<uint8_t*>(&duid_vector[0]));
2541  inbind[0].buffer_length = duid_length;
2542  inbind[0].length = &duid_length;
2543 
2544  // IAID
2545  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2546  inbind[1].buffer = reinterpret_cast<char*>(&iaid);
2547  inbind[1].is_unsigned = MLM_TRUE;
2548 
2549  // Subnet ID
2550  inbind[2].buffer_type = MYSQL_TYPE_LONG;
2551  inbind[2].buffer = reinterpret_cast<char*>(&subnet_id);
2552  inbind[2].is_unsigned = MLM_TRUE;
2553 
2554  // LEASE_TYPE
2555  inbind[3].buffer_type = MYSQL_TYPE_TINY;
2556  inbind[3].buffer = reinterpret_cast<char*>(&lease_type);
2557  inbind[3].is_unsigned = MLM_TRUE;
2558 
2559  // ... and get the data
2560  Lease6Collection result;
2561 
2562  // Get a context
2563  MySqlLeaseContextAlloc get_context(*this);
2564  MySqlLeaseContextPtr ctx = get_context.ctx_;
2565 
2566  getLeaseCollection(ctx, GET_LEASE6_DUID_IAID_SUBID, inbind, result);
2567 
2568  return (result);
2569 }
2570 
2574  .arg(subnet_id);
2575 
2576  // Set up the WHERE clause value
2577  MYSQL_BIND inbind[1];
2578  memset(inbind, 0, sizeof(inbind));
2579 
2580  // Subnet ID
2581  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2582  inbind[0].buffer = reinterpret_cast<char*>(&subnet_id);
2583  inbind[0].is_unsigned = MLM_TRUE;
2584 
2585  // ... and get the data
2586  Lease6Collection result;
2587 
2588  // Get a context
2589  MySqlLeaseContextAlloc get_context(*this);
2590  MySqlLeaseContextPtr ctx = get_context.ctx_;
2591 
2592  getLeaseCollection(ctx, GET_LEASE6_SUBID, inbind, result);
2593 
2594  return (result);
2595 }
2596 
2600 
2601  Lease6Collection result;
2602 
2603  // Get a context
2604  MySqlLeaseContextAlloc get_context(*this);
2605  MySqlLeaseContextPtr ctx = get_context.ctx_;
2606 
2607  getLeaseCollection(ctx, GET_LEASE6, 0, result);
2608 
2609  return (result);
2610 }
2611 
2613 MySqlLeaseMgr::getLeases6(const DUID& duid) const {
2615  .arg(duid.toText());
2616 
2617  // Set up the WHERE clause value
2618  MYSQL_BIND inbind[1];
2619  memset(inbind, 0, sizeof(inbind));
2620 
2621  const vector<uint8_t>& duid_vector = duid.getDuid();
2622  unsigned long duid_length = duid_vector.size();
2623 
2624  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2625  inbind[0].buffer = reinterpret_cast<char*>(
2626  const_cast<uint8_t*>(&duid_vector[0]));
2627  inbind[0].buffer_length = duid_length;
2628  inbind[0].length = &duid_length;
2629 
2630  Lease6Collection result;
2631 
2632  // Get a context
2633  MySqlLeaseContextAlloc get_context(*this);
2634  MySqlLeaseContextPtr ctx = get_context.ctx_;
2635 
2636  getLeaseCollection(ctx, GET_LEASE6_DUID, inbind, result);
2637 
2638  return result;
2639 }
2640 
2642 MySqlLeaseMgr::getLeases6(const std::string& hostname) const {
2644  .arg(hostname);
2645 
2646  // Set up the WHERE clause value
2647  MYSQL_BIND inbind[1];
2648  memset(inbind, 0, sizeof(inbind));
2649 
2650  // Hostname
2651  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2652  inbind[0].buffer = const_cast<char*>(hostname.c_str());
2653  inbind[0].buffer_length = hostname.length();
2654 
2655  // ... and get the data
2656  Lease6Collection result;
2657 
2658  // Get a context
2659  MySqlLeaseContextAlloc get_context(*this);
2660  MySqlLeaseContextPtr ctx = get_context.ctx_;
2661 
2662  getLeaseCollection(ctx, GET_LEASE6_HOSTNAME, inbind, result);
2663 
2664  return (result);
2665 }
2666 
2668 MySqlLeaseMgr::getLeases6(const IOAddress& lower_bound_address,
2669  const LeasePageSize& page_size) const {
2670  // Expecting IPv6 address.
2671  if (!lower_bound_address.isV6()) {
2672  isc_throw(InvalidAddressFamily, "expected IPv6 address while "
2673  "retrieving leases from the lease database, got "
2674  << lower_bound_address);
2675  }
2676 
2678  .arg(page_size.page_size_)
2679  .arg(lower_bound_address.toText());
2680 
2681  // Prepare WHERE clause
2682  MYSQL_BIND inbind[2];
2683  memset(inbind, 0, sizeof(inbind));
2684 
2685  // In IPv6 we compare addresses represented as strings. The IPv6 zero address
2686  // is ::, so it is greater than any other address. In this special case, we
2687  // just use 0 for comparison which should be lower than any real IPv6 address.
2688  std::string lb_address_data = "0";
2689  if (!lower_bound_address.isV6Zero()) {
2690  lb_address_data = lower_bound_address.toText();
2691  }
2692 
2693  // Bind lower bound address
2694  unsigned long lb_address_data_size = lb_address_data.size();
2695  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2696  inbind[0].buffer = const_cast<char*>(lb_address_data.c_str());
2697  inbind[0].buffer_length = lb_address_data_size;
2698  inbind[0].length = &lb_address_data_size;
2699 
2700  // Bind page size value
2701  size_t* ps = const_cast<size_t*>(&page_size.page_size_);
2702  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2703  inbind[1].buffer = reinterpret_cast<char*>(ps);
2704  inbind[1].is_unsigned = MLM_TRUE;
2705 
2706  // Get the leases
2707  Lease6Collection result;
2708 
2709  // Get a context
2710  MySqlLeaseContextAlloc get_context(*this);
2711  MySqlLeaseContextPtr ctx = get_context.ctx_;
2712 
2713  getLeaseCollection(ctx, GET_LEASE6_PAGE, inbind, result);
2714 
2715  return (result);
2716 }
2717 
2718 void
2720  const size_t max_leases) const {
2722  .arg(max_leases);
2723  getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
2724 }
2725 
2726 void
2728  const size_t max_leases) const {
2730  .arg(max_leases);
2731  getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
2732 }
2733 
2734 template<typename LeaseCollection>
2735 void
2736 MySqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
2737  const size_t max_leases,
2738  StatementIndex statement_index) const {
2739  // Set up the WHERE clause value
2740  MYSQL_BIND inbind[3];
2741  memset(inbind, 0, sizeof(inbind));
2742 
2743  // Exclude reclaimed leases.
2744  uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
2745  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2746  inbind[0].buffer = reinterpret_cast<char*>(&state);
2747  inbind[0].is_unsigned = MLM_TRUE;
2748 
2749  // Expiration timestamp.
2750  MYSQL_TIME expire_time;
2751  MySqlConnection::convertToDatabaseTime(time(NULL), expire_time);
2752  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2753  inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
2754  inbind[1].buffer_length = sizeof(expire_time);
2755 
2756  // If the number of leases is 0, we will return all leases. This is
2757  // achieved by setting the limit to a very high value.
2758  uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
2759  std::numeric_limits<uint32_t>::max();
2760  inbind[2].buffer_type = MYSQL_TYPE_LONG;
2761  inbind[2].buffer = reinterpret_cast<char*>(&limit);
2762  inbind[2].is_unsigned = MLM_TRUE;
2763 
2764  // Get a context
2765  MySqlLeaseContextAlloc get_context(*this);
2766  MySqlLeaseContextPtr ctx = get_context.ctx_;
2767 
2768  // Get the data
2769  getLeaseCollection(ctx, statement_index, inbind, expired_leases);
2770 }
2771 
2772 // Update lease methods. These comprise common code that handles the actual
2773 // update, and type-specific methods that set up the parameters for the prepared
2774 // statement depending on the type of lease.
2775 
2776 template <typename LeasePtr>
2777 void
2778 MySqlLeaseMgr::updateLeaseCommon(MySqlLeaseContextPtr& ctx,
2779  StatementIndex stindex,
2780  MYSQL_BIND* bind,
2781  const LeasePtr& lease) {
2782 
2783  // Bind the parameters to the statement
2784  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], bind);
2785  checkError(ctx, status, stindex, "unable to bind parameters");
2786 
2787  // Execute
2788  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2789  checkError(ctx, status, stindex, "unable to execute");
2790 
2791  // See how many rows were affected. The statement should only update a
2792  // single row.
2793  int affected_rows = mysql_stmt_affected_rows(ctx->conn_.statements_[stindex]);
2794 
2795  // Check success case first as it is the most likely outcome.
2796  if (affected_rows == 1) {
2797  return;
2798  }
2799 
2800  // If no rows affected, lease doesn't exist.
2801  if (affected_rows == 0) {
2802  isc_throw(NoSuchLease, "unable to update lease for address " <<
2803  lease->addr_.toText() << " as it does not exist");
2804  }
2805 
2806  // Should not happen - primary key constraint should only have selected
2807  // one row.
2808  isc_throw(DbOperationError, "apparently updated more than one lease "
2809  "that had the address " << lease->addr_.toText());
2810 }
2811 
2812 void
2814  const StatementIndex stindex = UPDATE_LEASE4;
2815 
2817  .arg(lease->addr_.toText());
2818 
2819  // Get a context
2820  MySqlLeaseContextAlloc get_context(*this);
2821  MySqlLeaseContextPtr ctx = get_context.ctx_;
2822 
2823  // Create the MYSQL_BIND array for the data being updated
2824  std::vector<MYSQL_BIND> bind = ctx->exchange4_->createBindForSend(lease);
2825 
2826  // Set up the WHERE clause and append it to the MYSQL_BIND array
2827  MYSQL_BIND inbind[2];
2828  memset(inbind, 0, sizeof(inbind));
2829 
2830  uint32_t addr4 = lease->addr_.toUint32();
2831  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2832  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
2833  inbind[0].is_unsigned = MLM_TRUE;
2834 
2835  bind.push_back(inbind[0]);
2836 
2837  MYSQL_TIME expire;
2838  MySqlConnection::convertToDatabaseTime(lease->current_cltt_,
2839  lease->current_valid_lft_,
2840  expire);
2841  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2842  inbind[1].buffer = reinterpret_cast<char*>(&expire);
2843  inbind[1].buffer_length = sizeof(expire);
2844 
2845  bind.push_back(inbind[1]);
2846 
2847  // Drop to common update code
2848  updateLeaseCommon(ctx, stindex, &bind[0], lease);
2849 
2850  // Update lease current expiration time.
2851  lease->updateCurrentExpirationTime();
2852 }
2853 
2854 void
2856  const StatementIndex stindex = UPDATE_LEASE6;
2857 
2859  .arg(lease->addr_.toText())
2860  .arg(lease->type_);
2861 
2862  // Get a context
2863  MySqlLeaseContextAlloc get_context(*this);
2864  MySqlLeaseContextPtr ctx = get_context.ctx_;
2865 
2866  // Create the MYSQL_BIND array for the data being updated
2867  std::vector<MYSQL_BIND> bind = ctx->exchange6_->createBindForSend(lease);
2868 
2869  // Set up the WHERE clause and append it to the MYSQL_BIND array
2870  MYSQL_BIND inbind[2];
2871  memset(inbind, 0, sizeof(inbind));
2872 
2873  std::string addr6 = lease->addr_.toText();
2874  unsigned long addr6_length = addr6.size();
2875 
2876  // See the earlier description of the use of "const_cast" when accessing
2877  // the address for an explanation of the reason.
2878  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2879  inbind[0].buffer = const_cast<char*>(addr6.c_str());
2880  inbind[0].buffer_length = addr6_length;
2881  inbind[0].length = &addr6_length;
2882 
2883  bind.push_back(inbind[0]);
2884 
2885  MYSQL_TIME expire;
2886  MySqlConnection::convertToDatabaseTime(lease->current_cltt_,
2887  lease->current_valid_lft_,
2888  expire);
2889  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2890  inbind[1].buffer = reinterpret_cast<char*>(&expire);
2891  inbind[1].buffer_length = sizeof(expire);
2892 
2893  bind.push_back(inbind[1]);
2894 
2895  // Drop to common update code
2896  updateLeaseCommon(ctx, stindex, &bind[0], lease);
2897 
2898  // Update lease current expiration time.
2899  lease->updateCurrentExpirationTime();
2900 }
2901 
2902 // Delete lease methods. Similar to other groups of methods, these comprise
2903 // a per-type method that sets up the relevant MYSQL_BIND array (in this
2904 // case, a single method for both V4 and V6 addresses) and a common method that
2905 // handles the common processing.
2906 
2907 uint64_t
2908 MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
2909  MYSQL_BIND* bind) {
2910 
2911  // Get a context
2912  MySqlLeaseContextAlloc get_context(*this);
2913  MySqlLeaseContextPtr ctx = get_context.ctx_;
2914 
2915  // Bind the input parameters to the statement
2916  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], bind);
2917  checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
2918 
2919  // Execute
2920  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2921  checkError(ctx, status, stindex, "unable to execute");
2922 
2923  // See how many rows were affected. Note that the statement may delete
2924  // multiple rows.
2925  return (static_cast<uint64_t>(mysql_stmt_affected_rows(ctx->conn_.statements_[stindex])));
2926 }
2927 
2928 bool
2930  const IOAddress& addr = lease->addr_;
2932  .arg(addr.toText());
2933 
2934  // Set up the WHERE clause value
2935  MYSQL_BIND inbind[2];
2936  memset(inbind, 0, sizeof(inbind));
2937 
2938  uint32_t addr4 = addr.toUint32();
2939 
2940  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2941  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
2942  inbind[0].is_unsigned = MLM_TRUE;
2943 
2944  MYSQL_TIME expire;
2945  MySqlConnection::convertToDatabaseTime(lease->current_cltt_,
2946  lease->current_valid_lft_,
2947  expire);
2948  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2949  inbind[1].buffer = reinterpret_cast<char*>(&expire);
2950  inbind[1].buffer_length = sizeof(expire);
2951 
2952  auto affected_rows = deleteLeaseCommon(DELETE_LEASE4, inbind);
2953 
2954  // Check success case first as it is the most likely outcome.
2955  if (affected_rows == 1) {
2956  return (true);
2957  }
2958 
2959  // If no rows affected, lease doesn't exist.
2960  if (affected_rows == 0) {
2961  return (false);
2962  }
2963 
2964  // Should not happen - primary key constraint should only have selected
2965  // one row.
2966  isc_throw(DbOperationError, "apparently deleted more than one lease "
2967  "that had the address " << lease->addr_.toText());
2968 }
2969 
2970 bool
2972  const IOAddress& addr = lease->addr_;
2975  .arg(addr.toText());
2976 
2977  // Set up the WHERE clause value
2978  MYSQL_BIND inbind[2];
2979  memset(inbind, 0, sizeof(inbind));
2980 
2981  std::string addr6 = addr.toText();
2982  unsigned long addr6_length = addr6.size();
2983 
2984  // See the earlier description of the use of "const_cast" when accessing
2985  // the address for an explanation of the reason.
2986  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2987  inbind[0].buffer = const_cast<char*>(addr6.c_str());
2988  inbind[0].buffer_length = addr6_length;
2989  inbind[0].length = &addr6_length;
2990 
2991  MYSQL_TIME expire;
2992  MySqlConnection::convertToDatabaseTime(lease->current_cltt_,
2993  lease->current_valid_lft_,
2994  expire);
2995  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2996  inbind[1].buffer = reinterpret_cast<char*>(&expire);
2997  inbind[1].buffer_length = sizeof(expire);
2998 
2999  auto affected_rows = deleteLeaseCommon(DELETE_LEASE6, inbind);
3000 
3001  // Check success case first as it is the most likely outcome.
3002  if (affected_rows == 1) {
3003  return (true);
3004  }
3005 
3006  // If no rows affected, lease doesn't exist.
3007  if (affected_rows == 0) {
3008  return (false);
3009  }
3010 
3011  // Should not happen - primary key constraint should only have selected
3012  // one row.
3013  isc_throw(DbOperationError, "apparently deleted more than one lease "
3014  "that had the address " << lease->addr_.toText());
3015 }
3016 
3017 uint64_t
3020  .arg(secs);
3021  return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
3022 }
3023 
3024 uint64_t
3027  .arg(secs);
3028  return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
3029 }
3030 
3031 uint64_t
3032 MySqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
3033  StatementIndex statement_index) {
3034  // Set up the WHERE clause value
3035  MYSQL_BIND inbind[2];
3036  memset(inbind, 0, sizeof(inbind));
3037 
3038  // State is reclaimed.
3039  uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
3040  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3041  inbind[0].buffer = reinterpret_cast<char*>(&state);
3042  inbind[0].is_unsigned = MLM_TRUE;
3043 
3044  // Expiration timestamp.
3045  MYSQL_TIME expire_time;
3046  MySqlConnection::convertToDatabaseTime(time(NULL) - static_cast<time_t>(secs), expire_time);
3047  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
3048  inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
3049  inbind[1].buffer_length = sizeof(expire_time);
3050 
3051  // Get the number of deleted leases and log it.
3052  uint64_t deleted_leases = deleteLeaseCommon(statement_index, inbind);
3054  .arg(deleted_leases);
3055 
3056  return (deleted_leases);
3057 }
3058 
3061  // Get a context
3062  MySqlLeaseContextAlloc get_context(*this);
3063  MySqlLeaseContextPtr ctx = get_context.ctx_;
3064 
3065  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3067  false));
3068  query->start();
3069  return(query);
3070 }
3071 
3074  // Get a context
3075  MySqlLeaseContextAlloc get_context(*this);
3076  MySqlLeaseContextPtr ctx = get_context.ctx_;
3077 
3078  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3080  false,
3081  subnet_id));
3082  query->start();
3083  return(query);
3084 }
3085 
3088  const SubnetID& last_subnet_id) {
3089  // Get a context
3090  MySqlLeaseContextAlloc get_context(*this);
3091  MySqlLeaseContextPtr ctx = get_context.ctx_;
3092 
3093  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3095  false,
3096  first_subnet_id,
3097  last_subnet_id));
3098  query->start();
3099  return(query);
3100 }
3101 
3104  // Get a context
3105  MySqlLeaseContextAlloc get_context(*this);
3106  MySqlLeaseContextPtr ctx = get_context.ctx_;
3107 
3108  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3110  true));
3111  query->start();
3112  return(query);
3113 }
3114 
3117  // Get a context
3118  MySqlLeaseContextAlloc get_context(*this);
3119  MySqlLeaseContextPtr ctx = get_context.ctx_;
3120 
3121  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3123  true,
3124  subnet_id));
3125  query->start();
3126  return(query);
3127 }
3128 
3131  const SubnetID& last_subnet_id) {
3132  // Get a context
3133  MySqlLeaseContextAlloc get_context(*this);
3134  MySqlLeaseContextPtr ctx = get_context.ctx_;
3135 
3136  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3138  true,
3139  first_subnet_id,
3140  last_subnet_id));
3141  query->start();
3142  return(query);
3143 }
3144 
3145 size_t
3146 MySqlLeaseMgr::wipeLeases4(const SubnetID& /*subnet_id*/) {
3147  isc_throw(NotImplemented, "wipeLeases4 is not implemented for MySQL backend");
3148 }
3149 
3150 size_t
3151 MySqlLeaseMgr::wipeLeases6(const SubnetID& /*subnet_id*/) {
3152  isc_throw(NotImplemented, "wipeLeases6 is not implemented for MySQL backend");
3153 }
3154 
3155 // Miscellaneous database methods.
3156 
3157 std::string
3159  // Get a context
3160  MySqlLeaseContextAlloc get_context(*this);
3161  MySqlLeaseContextPtr ctx = get_context.ctx_;
3162 
3163  std::string name = "";
3164  try {
3165  name = ctx->conn_.getParameter("name");
3166  } catch (...) {
3167  // Return an empty name
3168  }
3169  return (name);
3170 }
3171 
3172 std::string
3174  return (std::string("MySQL Database"));
3175 }
3176 
3177 std::pair<uint32_t, uint32_t>
3180 
3181  return (MySqlConnection::getVersion(parameters_));
3182 }
3183 
3184 void
3187 }
3188 
3189 void
3192 }
3193 
3194 void
3195 MySqlLeaseMgr::checkError(MySqlLeaseContextPtr& ctx,
3196  int status, StatementIndex index,
3197  const char* what) const {
3198  ctx->conn_.checkError(status, index, what);
3199 }
3200 
3201 } // namespace dhcp
3202 } // namespace isc
static bool dbReconnect(db::ReconnectCtlPtr db_reconnect_ctl)
Attempts to reconnect the server to the lease DB backend manager.
RAII class creating a critical section.
const isc::log::MessageID DHCPSRV_MYSQL_GET_EXPIRED4
const std::vector< uint8_t > & getDuid() const
Returns a const reference to the actual DUID value.
Definition: duid.cc:46
const isc::log::MessageID DHCPSRV_MYSQL_ADD_ADDR4
void start()
Creates the IPv4 lease statistical data result set.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
bool my_bool
my_bool type in MySQL 8.x.
boost::shared_ptr< LeaseStatsQuery > LeaseStatsQueryPtr
Defines a pointer to a LeaseStatsQuery.
Definition: lease_mgr.h:207
const isc::log::MessageID DHCPSRV_MYSQL_GET_IAID_SUBID_DUID
std::string toText() const
Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
Definition: duid.cc:122
Fetch and Release MySQL Results.
A generic exception that is thrown when a function is not implemented.
const size_t USER_CONTEXT_MAX_LEN
Maximum length of user context.
Definition: host.h:54
static const uint32_t STATE_EXPIRED_RECLAIMED
Expired and reclaimed lease.
Definition: lease.h:79
static std::string getDBVersion()
Local version of getDBVersion() class method.
const isc::log::MessageID DHCPSRV_MYSQL_DELETE_ADDR
static void convertToDatabaseTime(const time_t input_time, MYSQL_TIME &output_time)
Convert time_t value to database time.
const isc::log::MessageID DHCPSRV_MYSQL_GET_HOSTNAME6
virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs)
Deletes all expired-reclaimed DHCPv6 leases.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
virtual std::string getName() const
Returns backend name.
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
const isc::log::MessageID DHCPSRV_MYSQL_GET4
Data is truncated.
Definition: db_exceptions.h:35
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap &parameters)
Get the schema version.
static void setErrorIndicators(MYSQL_BIND *bind, my_bool *error, size_t count)
Set error indicators.
MySQL Lease Context Pool.
const isc::log::MessageID DHCPSRV_MYSQL_GET_PAGE6
static void destroy()
Destroy lease manager.
const isc::log::MessageID DHCPSRV_MYSQL_NEGATIVE_LEASES_STAT
MySqlLeaseStatsQuery(MySqlConnection &conn, const size_t statement_index, const bool fetch_type, const SubnetID &subnet_id)
Constructor to query for a single subnet's stats.
virtual size_t wipeLeases6(const SubnetID &subnet_id)
Removed specified IPv6 leases.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
std::string getErrorColumns()
Return columns in error.
Attempt to update lease that was not there.
std::vector< Lease4Ptr > Lease4Collection
A collection of IPv4 leases.
Definition: lease.h:487
const uint32_t MYSQL_SCHEMA_VERSION_MAJOR
const isc::log::MessageID DHCPSRV_MYSQL_DELETED_EXPIRED_RECLAIMED
virtual void updateLease6(const Lease6Ptr &lease6)
Updates IPv6 lease.
const isc::log::MessageID DHCPSRV_MYSQL_GET_SUBID4
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
std::vector< uint8_t > hwaddr_
Definition: hwaddr.h:98
STL namespace.
Base class for fulfilling a statistical lease data query.
Definition: lease_mgr.h:128
const uint32_t MYSQL_SCHEMA_VERSION_MINOR
std::string toText(bool include_htype=true) const
Returns textual representation of a hardware address (e.g.
Definition: hwaddr.cc:51
const isc::log::MessageID DHCPSRV_MYSQL_GET_HWADDR
const isc::log::MessageID DHCPSRV_MYSQL_UPDATE_ADDR4
virtual LeaseStatsQueryPtr startLeaseStatsQuery4()
Creates and runs the IPv4 lease stats query.
virtual size_t wipeLeases4(const SubnetID &subnet_id)
Removes specified IPv4 leases.
const isc::log::MessageID DHCPSRV_MYSQL_GET_DUID
virtual std::string getDescription() const
Returns description of the backend.
boost::shared_ptr< Lease > LeasePtr
Pointer to the lease object.
Definition: lease.h:26
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const
Returns existing IPv6 lease for a given IPv6 address.
Holds DUID (DHCPv6 Unique Identifier)
Definition: duid.h:27
virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs)
Deletes all expired-reclaimed DHCPv4 leases.
Exception thrown on failure to open database.
const isc::log::MessageID DHCPSRV_MYSQL_GET_PAGE4
const isc::log::MessageID DHCPSRV_MYSQL_GET_HOSTNAME4
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
Definition: lease_mgr.h:758
virtual Lease4Collection getLeases4() const
Returns all IPv4 leases.
const isc::log::MessageID DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED4
std::vector< MYSQL_BIND > createBindForReceive()
Create BIND array to receive data.
virtual void rollback()
Rollback Transactions.
virtual bool addLease(const Lease4Ptr &lease)
Adds an IPv4 lease.
int MysqlExecuteStatement(MYSQL_STMT *stmt)
Execute a prepared statement.
Multiple lease records found where one expected.
Definition: db_exceptions.h:28
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery6(const SubnetID &subnet_id)
Creates and runs the IPv6 lease stats query for a single subnet.
MySQL Lease Context.
Definition: edns.h:19
const my_bool MLM_FALSE
MySQL false value.
std::vector< MYSQL_BIND > createBindForSend(const Lease4Ptr &lease)
Create MYSQL_BIND objects for Lease4 Pointer.
const size_t page_size_
Holds page size.
Definition: lease_mgr.h:53
StatementIndex
Statement Tags.
boost::shared_ptr< MySqlLeaseContext > MySqlLeaseContextPtr
Type of pointers to contexts.
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:283
bool getNextRow(LeaseStatsRow &row)
Fetches the next row in the result set.
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
virtual std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
const std::vector< uint8_t > & getClientId() const
Returns reference to the client-id data.
Definition: duid.cc:117
SubnetID subnet_id_
The subnet ID to which this data applies.
Definition: lease_mgr.h:114
const isc::log::MessageID DHCPSRV_MYSQL_GET_ADDR6
A generic exception that is thrown when an unexpected error condition occurs.
virtual ~MySqlLeaseMgr()
Destructor (closes database)
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
std::vector< MYSQL_BIND > createBindForSend(const Lease6Ptr &lease)
Create MYSQL_BIND objects for Lease6 Pointer.
const isc::log::MessageID DHCPSRV_MYSQL_GET_ADDR4
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition: lease.h:640
virtual void updateLease4(const Lease4Ptr &lease4)
Updates IPv4 lease.
std::string toText() const
Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
Definition: duid.cc:75
MySql derivation of the statistical lease data query.
const isc::log::MessageID DHCPSRV_MYSQL_ROLLBACK
MySQL Lease Manager.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const isc::log::MessageID DHCPSRV_MYSQL_ADD_ADDR6
const isc::log::MessageID DHCPSRV_MYSQL_GET_VERSION
const isc::log::MessageID DHCPSRV_MYSQL_GET_SUBID_CLIENTID
virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery6(const SubnetID &first_subnet_id, const SubnetID &last_subnet_id)
Creates and runs the IPv6 lease stats query for a single subnet.
const int MLM_MYSQL_FETCH_SUCCESS
check for bool size
Ethernet 10Mbps.
Definition: dhcp4.h:56
Lease6Ptr getLeaseData()
Copy Received Data into Lease6 Object.
MySqlLeaseStatsQuery(MySqlConnection &conn, const size_t statement_index, const bool fetch_type)
Constructor to query for all subnets' stats.
virtual bool deleteLease(const Lease4Ptr &lease)
Deletes an IPv4 lease.
virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery4(const SubnetID &subnet_id)
Creates and runs the IPv4 lease stats query for a single subnet.
Invalid address family used as input to Lease Manager.
Definition: db_exceptions.h:71
const isc::log::MessageID DHCPSRV_MYSQL_COMMIT
static bool invokeDbFailedCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restore failed connectivity callback.
const isc::log::MessageID DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED6
virtual void getExpiredLeases4(Lease4Collection &expired_leases, const size_t max_leases) const
Returns a collection of expired DHCPv4 leases.
Defines the logger used by the top-level component of kea-dhcp-ddns.
std::function< bool(ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
const isc::log::MessageID DHCPSRV_MYSQL_LEASE_DB_RECONNECT_ATTEMPT_FAILED
Common MySQL and Lease Data Methods.
static bool invokeDbRecoveredCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restored connectivity callback.
Lease4Ptr getLeaseData()
Copy Received Data into Lease4 Object.
Exchange MySQL and Lease6 Data.
Exchange MySQL and Lease4 Data.
virtual Lease6Collection getLeases6() const
Returns all IPv6 leases.
static bool invokeDbLostCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's lost connectivity callback.
uint32_t lease_state_
The lease_state to which the count applies.
Definition: lease_mgr.h:118
Type
Type of lease or pool.
Definition: lease.h:50
Holds Client identifier or client IPv4 address.
Definition: duid.h:111
static void create(const std::string &dbaccess)
Create an instance of a lease manager.
const isc::log::MessageID DHCPSRV_MYSQL_LEASE_DB_RECONNECT_FAILED
virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery4(const SubnetID &first_subnet_id, const SubnetID &last_subnet_id)
Creates and runs the IPv4 lease stats query for a single subnet.
const isc::log::MessageID DHCPSRV_MYSQL_GET_SUBID6
MySqlLeaseStatsQuery(MySqlConnection &conn, const size_t statement_index, const bool fetch_type, const SubnetID &first_subnet_id, const SubnetID &last_subnet_id)
Constructor to query for the stats for a range of subnets.
const isc::log::MessageID DHCPSRV_MYSQL_GET_IAID_DUID
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const
Returns an IPv4 lease for specified IPv4 address.
Hardware type that represents information from DHCPv4 packet.
Definition: hwaddr.h:20
const isc::log::MessageID DHCPSRV_MYSQL_GET_CLIENTID
MySqlLeaseContextPtr createContext() const
Create a new context.
std::vector< MYSQL_BIND > createBindForReceive()
Create BIND array to receive data.
int64_t state_count_
state_count The count of leases in the lease state
Definition: lease_mgr.h:120
virtual void getExpiredLeases6(Lease6Collection &expired_leases, const size_t max_leases) const
Returns a collection of expired DHCPv6 leases.
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
Contains a single row of lease statistical data.
Definition: lease_mgr.h:61
virtual LeaseStatsQueryPtr startLeaseStatsQuery6()
Creates and runs the IPv6 lease stats query.
Wraps value holding size of the page with leases.
Definition: lease_mgr.h:43
const isc::log::MessageID DHCPSRV_MYSQL_GET_EXPIRED6
Lease::Type lease_type_
The lease_type to which the count applies.
Definition: lease_mgr.h:116
std::function< isc::asiolink::IOServicePtr()> IOServiceAccessor
Function which returns the IOService that can be used to recover the connection.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition: timer_mgr.cc:441
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
const isc::log::MessageID DHCPSRV_MYSQL_LEASE_DB_RECONNECT_ATTEMPT_SCHEDULE
virtual ~MySqlLeaseStatsQuery()
Destructor.
const isc::log::MessageID DHCPSRV_MYSQL_UPDATE_ADDR6
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition: lease.h:492
virtual void commit()
Commit Transactions.
MySqlLeaseMgr(const db::DatabaseConnection::ParameterMap &parameters)
Constructor.
static std::string getColumnsInError(my_bool *error, std::string *names, size_t count)
Return columns in error.
const isc::log::MessageID DHCPSRV_MYSQL_GET_SUBID_HWADDR
Exception thrown on failure to execute a database function.
const isc::log::MessageID DHCPSRV_MYSQL_GET6
const my_bool MLM_TRUE
MySQL true value.
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
Common MySQL Connector Pool.
std::string getErrorColumns()
Return columns in error.