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