Kea  2.3.1-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  {MySqlLeaseMgr::GET_LEASE4_COUNT_BY_CLASS,
336  "SELECT leases "
337  "FROM lease4_stat_by_client_class "
338  "WHERE client_class = ?"},
339  {MySqlLeaseMgr::GET_LEASE6_COUNT_BY_CLASS,
340  "SELECT leases "
341  "FROM lease6_stat_by_client_class "
342  "WHERE client_class = ? AND lease_type = ?"},
343 } }; // tagged_statements
344 
345 } // namespace
346 
347 namespace isc {
348 namespace dhcp {
349 
356 
358 public:
359 
371  static void setErrorIndicators(MYSQL_BIND* bind, my_bool* error,
372  size_t count) {
373  for (size_t i = 0; i < count; ++i) {
374  error[i] = MLM_FALSE;
375  bind[i].error = reinterpret_cast<my_bool*>(&error[i]);
376  }
377  }
378 
392  static std::string getColumnsInError(my_bool* error, std::string* names,
393  size_t count) {
394  std::string result = "";
395 
396  // Accumulate list of column names
397  for (size_t i = 0; i < count; ++i) {
398  if (error[i] == MLM_TRUE) {
399  if (!result.empty()) {
400  result += ", ";
401  }
402  result += names[i];
403  }
404  }
405 
406  if (result.empty()) {
407  result = "(None)";
408  }
409 
410  return (result);
411  }
412 };
413 
426 
429  static const size_t LEASE_COLUMNS = 11;
430 
431 public:
432 
437  MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), hwaddr_null_(MLM_FALSE),
438  client_id_length_(0), client_id_null_(MLM_FALSE),
439  subnet_id_(0), valid_lifetime_(0),
440  fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0),
441  state_(0), user_context_length_(0),
442  user_context_null_(MLM_FALSE) {
443  memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
444  memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
445  memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
446  memset(user_context_, 0, sizeof(user_context_));
447  std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
448 
449  // Set the column names (for error messages)
450  columns_[0] = "address";
451  columns_[1] = "hwaddr";
452  columns_[2] = "client_id";
453  columns_[3] = "valid_lifetime";
454  columns_[4] = "expire";
455  columns_[5] = "subnet_id";
456  columns_[6] = "fqdn_fwd";
457  columns_[7] = "fqdn_rev";
458  columns_[8] = "hostname";
459  columns_[9] = "state";
460  columns_[10] = "user_context";
461  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
462  }
463 
473  std::vector<MYSQL_BIND> createBindForSend(const Lease4Ptr& lease) {
474 
475  // Store lease object to ensure it remains valid.
476  lease_ = lease;
477 
478  // Initialize prior to constructing the array of MYSQL_BIND structures.
479  // It sets all fields, including is_null, to zero, so we need to set
480  // is_null only if it should be true. This gives up minor performance
481  // benefit while being safe approach. For improved readability, the
482  // code that explicitly sets is_null is there, but is commented out.
483  memset(bind_, 0, sizeof(bind_));
484 
485  // Set up the structures for the various components of the lease4
486  // structure.
487 
488  try {
489  // address: uint32_t
490  // The address in the Lease structure is an IOAddress object. Convert
491  // this to an integer for storage.
492  addr4_ = lease_->addr_.toUint32();
493  bind_[0].buffer_type = MYSQL_TYPE_LONG;
494  bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
495  bind_[0].is_unsigned = MLM_TRUE;
496  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
497  // reasons, see memset() above
498 
499  // hwaddr: varbinary(20) - hardware/MAC address
500  HWAddrPtr hwaddr = lease_->hwaddr_;
501  if (hwaddr) {
502  hwaddr_ = hwaddr->hwaddr_;
503  hwaddr_length_ = hwaddr->hwaddr_.size();
504 
505  // Make sure that the buffer has at least length of 1, even if
506  // empty HW address is passed. This is required by some of the
507  // MySQL connectors that the buffer is set to non-null value.
508  // Otherwise, null value would be inserted into the database,
509  // rather than empty string.
510  if (hwaddr_.empty()) {
511  hwaddr_.resize(1);
512  }
513 
514  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
515  bind_[1].buffer = reinterpret_cast<char*>(&(hwaddr_[0]));
516  bind_[1].buffer_length = hwaddr_length_;
517  bind_[1].length = &hwaddr_length_;
518  } else {
519  bind_[1].buffer_type = MYSQL_TYPE_NULL;
520  // According to http://dev.mysql.com/doc/refman/5.5/en/
521  // c-api-prepared-statement-data-structures.html, the other
522  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
523  // but let's set them to some sane values in case earlier versions
524  // didn't have that assumption.
525  hwaddr_null_ = MLM_TRUE;
526  bind_[1].buffer = NULL;
527  bind_[1].is_null = &hwaddr_null_;
528  }
529 
530  // client_id: varbinary(128)
531  if (lease_->client_id_) {
532  client_id_ = lease_->client_id_->getClientId();
533  client_id_length_ = client_id_.size();
534 
535  // Make sure that the buffer has at least length of 1, even if
536  // empty client id is passed. This is required by some of the
537  // MySQL connectors that the buffer is set to non-null value.
538  // Otherwise, null value would be inserted into the database,
539  // rather than empty string.
540  if (client_id_.empty()) {
541  client_id_.resize(1);
542  }
543 
544  bind_[2].buffer_type = MYSQL_TYPE_BLOB;
545  bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
546  bind_[2].buffer_length = client_id_length_;
547  bind_[2].length = &client_id_length_;
548  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
549  // reasons, see memset() above
550  } else {
551  bind_[2].buffer_type = MYSQL_TYPE_NULL;
552  // According to http://dev.mysql.com/doc/refman/5.5/en/
553  // c-api-prepared-statement-data-structures.html, the other
554  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
555  // but let's set them to some sane values in case earlier versions
556  // didn't have that assumption.
557  client_id_null_ = MLM_TRUE;
558  bind_[2].buffer = NULL;
559  bind_[2].is_null = &client_id_null_;
560  }
561 
562  // valid lifetime: unsigned int
563  bind_[3].buffer_type = MYSQL_TYPE_LONG;
564  bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
565  bind_[3].is_unsigned = MLM_TRUE;
566  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
567  // reasons, see memset() above
568 
569  // expire: timestamp
570  // The lease structure holds the client last transmission time (cltt_)
571  // For convenience for external tools, this is converted to lease
572  // expiry time (expire). The relationship is given by:
573  //
574  // expire = cltt_ + valid_lft_
575  // Avoid overflow with infinite valid lifetime by using
576  // expire = cltt_ when valid_lft_ = 0xffffffff
577  uint32_t valid_lft = lease_->valid_lft_;
578  if (valid_lft == Lease::INFINITY_LFT) {
579  valid_lft = 0;
580  }
581  MySqlConnection::convertToDatabaseTime(lease_->cltt_, valid_lft,
582  expire_);
583  bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
584  bind_[4].buffer = reinterpret_cast<char*>(&expire_);
585  bind_[4].buffer_length = sizeof(expire_);
586  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
587  // reasons, see memset() above
588 
589  // subnet_id: unsigned int
590  // Can use lease_->subnet_id_ directly as it is of type uint32_t.
591  bind_[5].buffer_type = MYSQL_TYPE_LONG;
592  bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
593  bind_[5].is_unsigned = MLM_TRUE;
594  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
595  // reasons, see memset() above
596 
597  // fqdn_fwd: boolean
598  bind_[6].buffer_type = MYSQL_TYPE_TINY;
599  bind_[6].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
600  bind_[6].is_unsigned = MLM_TRUE;
601  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
602  // reasons, see memset() above
603 
604  // fqdn_rev: boolean
605  bind_[7].buffer_type = MYSQL_TYPE_TINY;
606  bind_[7].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
607  bind_[7].is_unsigned = MLM_TRUE;
608  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
609  // reasons, see memset() above
610 
611  // hostname: varchar(255)
612  // Note that previously we used MYSQL_TYPE_VARCHAR instead of
613  // MYSQL_TYPE_STRING. However, that caused 'buffer type not supported'
614  // errors on some systems running MariaDB.
615  bind_[8].buffer_type = MYSQL_TYPE_STRING;
616  bind_[8].buffer = const_cast<char*>(lease_->hostname_.c_str());
617  bind_[8].buffer_length = lease_->hostname_.length();
618  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
619  // reasons, see memset() above
620 
621  // state: uint32_t
622  bind_[9].buffer_type = MYSQL_TYPE_LONG;
623  bind_[9].buffer = reinterpret_cast<char*>(&lease_->state_);
624  bind_[9].is_unsigned = MLM_TRUE;
625  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
626  // reasons, see memset() above
627 
628  // user_context: text
629  ConstElementPtr ctx = lease->getContext();
630  if (ctx) {
631  bind_[10].buffer_type = MYSQL_TYPE_STRING;
632  std::string ctx_txt = ctx->str();
633  strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
634  bind_[10].buffer = user_context_;
635  bind_[10].buffer_length = ctx_txt.length();
636  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
637  // reasons, see memset() above
638  } else {
639  bind_[10].buffer_type = MYSQL_TYPE_NULL;
640  }
641 
642  // Add the error flags
643  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
644 
645  // .. and check that we have the numbers correct at compile time.
646  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
647 
648  } catch (const std::exception& ex) {
650  "Could not create bind array from Lease4: "
651  << lease_->addr_.toText() << ", reason: " << ex.what());
652  }
653 
654  // Add the data to the vector. Note the end element is one after the
655  // end of the array.
656  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
657  }
658 
664  std::vector<MYSQL_BIND> createBindForReceive() {
665 
666  // Initialize MYSQL_BIND array.
667  // It sets all fields, including is_null, to zero, so we need to set
668  // is_null only if it should be true. This gives up minor performance
669  // benefit while being safe approach. For improved readability, the
670  // code that explicitly sets is_null is there, but is commented out.
671  memset(bind_, 0, sizeof(bind_));
672 
673  // address: uint32_t
674  bind_[0].buffer_type = MYSQL_TYPE_LONG;
675  bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
676  bind_[0].is_unsigned = MLM_TRUE;
677  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
678  // reasons, see memset() above
679 
680  // hwaddr: varbinary(20)
681  hwaddr_length_ = sizeof(hwaddr_buffer_);
682  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
683  bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
684  bind_[1].buffer_length = hwaddr_length_;
685  bind_[1].length = &hwaddr_length_;
686  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
687  // reasons, see memset() above
688 
689  // client_id: varbinary(128)
690  client_id_length_ = sizeof(client_id_buffer_);
691  bind_[2].buffer_type = MYSQL_TYPE_BLOB;
692  bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
693  bind_[2].buffer_length = client_id_length_;
694  bind_[2].length = &client_id_length_;
695  bind_[2].is_null = &client_id_null_;
696  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
697  // reasons, see memset() above
698 
699  // valid lifetime: unsigned int
700  bind_[3].buffer_type = MYSQL_TYPE_LONG;
701  bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
702  bind_[3].is_unsigned = MLM_TRUE;
703  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
704  // reasons, see memset() above
705 
706  // expire: timestamp
707  bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
708  bind_[4].buffer = reinterpret_cast<char*>(&expire_);
709  bind_[4].buffer_length = sizeof(expire_);
710  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
711  // reasons, see memset() above
712 
713  // subnet_id: unsigned int
714  bind_[5].buffer_type = MYSQL_TYPE_LONG;
715  bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
716  bind_[5].is_unsigned = MLM_TRUE;
717  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
718  // reasons, see memset() above
719 
720  // fqdn_fwd: boolean
721  bind_[6].buffer_type = MYSQL_TYPE_TINY;
722  bind_[6].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
723  bind_[6].is_unsigned = MLM_TRUE;
724  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
725  // reasons, see memset() above
726 
727  // fqdn_rev: boolean
728  bind_[7].buffer_type = MYSQL_TYPE_TINY;
729  bind_[7].buffer = reinterpret_cast<char*>(&fqdn_rev_);
730  bind_[7].is_unsigned = MLM_TRUE;
731  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
732  // reasons, see memset() above
733 
734  // hostname: varchar(255)
735  // Note that previously we used MYSQL_TYPE_VARCHAR instead of
736  // MYSQL_TYPE_STRING. However, that caused 'buffer type not supported'
737  // errors on some systems running MariaDB.
738  hostname_length_ = sizeof(hostname_buffer_);
739  bind_[8].buffer_type = MYSQL_TYPE_STRING;
740  bind_[8].buffer = reinterpret_cast<char*>(hostname_buffer_);
741  bind_[8].buffer_length = hostname_length_;
742  bind_[8].length = &hostname_length_;
743  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
744  // reasons, see memset() above
745 
746  // state: uint32_t
747  bind_[9].buffer_type = MYSQL_TYPE_LONG;
748  bind_[9].buffer = reinterpret_cast<char*>(&state_);
749  bind_[9].is_unsigned = MLM_TRUE;
750  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
751  // reasons, see memset() above
752 
753  // user_context: text
754  user_context_null_ = MLM_FALSE;
755  user_context_length_ = sizeof(user_context_);
756  bind_[10].buffer_type = MYSQL_TYPE_STRING;
757  bind_[10].buffer = reinterpret_cast<char*>(user_context_);
758  bind_[10].buffer_length = user_context_length_;
759  bind_[10].length = &user_context_length_;
760  bind_[10].is_null = &user_context_null_;
761 
762  // Add the error flags
763  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
764 
765  // .. and check that we have the numbers correct at compile time.
766  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
767 
768  // Add the data to the vector. Note the end element is one after the
769  // end of the array.
770  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
771  }
772 
782  // Convert times received from the database to times for the lease
783  // structure. See the expire code of createBindForSend for
784  // the infinite valid lifetime special case.
785  time_t cltt = 0;
786  // Recover from overflow
787  uint32_t valid_lft = valid_lifetime_;
788  if (valid_lft == Lease::INFINITY_LFT) {
789  valid_lft = 0;
790  }
791  MySqlConnection::convertFromDatabaseTime(expire_, valid_lft, cltt);
792 
793  if (client_id_null_ == MLM_TRUE) {
794  // There's no client-id, so we pass client-id_length_ set to 0
795  client_id_length_ = 0;
796  }
797 
798  // Hostname is passed to Lease4 as a string object. We have to create
799  // it from the buffer holding hostname and the buffer length.
800  std::string hostname(hostname_buffer_,
801  hostname_buffer_ + hostname_length_);
802 
803  // Set hardware address if it was set
804  HWAddrPtr hwaddr;
805  if (hwaddr_null_ == MLM_FALSE) {
806  hwaddr.reset(new HWAddr(hwaddr_buffer_, hwaddr_length_, HTYPE_ETHER));
807  }
808 
809  // Convert user_context to string as well.
810  std::string user_context;
811  if (user_context_null_ == MLM_FALSE) {
812  user_context_[user_context_length_] = '\0';
813  user_context.assign(user_context_);
814  }
815 
816  // Set the user context if there is one.
817  ConstElementPtr ctx;
818  if (!user_context.empty()) {
819  ctx = Element::fromJSON(user_context);
820  if (!ctx || (ctx->getType() != Element::map)) {
821  isc_throw(BadValue, "user context '" << user_context
822  << "' is not a JSON map");
823  }
824  }
825 
826  Lease4Ptr lease(boost::make_shared<Lease4>(addr4_, hwaddr,
827  client_id_buffer_,
828  client_id_length_,
829  valid_lifetime_, cltt,
830  subnet_id_, fqdn_fwd_,
831  fqdn_rev_, hostname));
832 
833  // Set state.
834  lease->state_ = state_;
835 
836  if (ctx) {
837  lease->setContext(ctx);
838  }
839 
840  return (lease);
841  }
842 
853  std::string getErrorColumns() {
854  return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
855  }
856 
857 private:
858 
859  // Note: All array lengths are equal to the corresponding variable in the
860  // schema.
861  // Note: Arrays are declared fixed length for speed of creation
862  uint32_t addr4_;
863  MYSQL_BIND bind_[LEASE_COLUMNS];
864  std::string columns_[LEASE_COLUMNS];
865  my_bool error_[LEASE_COLUMNS];
866  Lease4Ptr lease_;
867  std::vector<uint8_t> hwaddr_;
868  uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
869  unsigned long hwaddr_length_;
870  my_bool hwaddr_null_;
871  std::vector<uint8_t> client_id_;
872  uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
873  unsigned long client_id_length_;
874  my_bool client_id_null_;
875  MYSQL_TIME expire_;
876  uint32_t subnet_id_;
877  uint32_t valid_lifetime_;
878  my_bool fqdn_fwd_;
879  my_bool fqdn_rev_;
880  char hostname_buffer_[HOSTNAME_MAX_LEN];
881  unsigned long hostname_length_;
882  uint32_t state_;
883  char user_context_[USER_CONTEXT_MAX_LEN];
884  unsigned long user_context_length_;
885  my_bool user_context_null_;
886 };
887 
900 
903  static const size_t LEASE_COLUMNS = 17;
904 
905 public:
906 
911  MySqlLease6Exchange() : addr6_length_(0), hwaddr_length_(0),
912  hwaddr_null_(MLM_FALSE), duid_length_(0),
913  iaid_(0), lease_type_(0), prefix_len_(0),
914  pref_lifetime_(0), subnet_id_(0), valid_lifetime_(0),
915  fqdn_fwd_(false), fqdn_rev_(false),
916  hostname_length_(0), hwtype_(0), hwaddr_source_(0),
917  state_(0), user_context_length_(0),
918  user_context_null_(MLM_FALSE) {
919  memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
920  memset(duid_buffer_, 0, sizeof(duid_buffer_));
921  memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
922  memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
923  memset(user_context_, 0, sizeof(user_context_));
924  std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
925 
926  // Set the column names (for error messages)
927  columns_[0] = "address";
928  columns_[1] = "duid";
929  columns_[2] = "valid_lifetime";
930  columns_[3] = "expire";
931  columns_[4] = "subnet_id";
932  columns_[5] = "pref_lifetime";
933  columns_[6] = "lease_type";
934  columns_[7] = "iaid";
935  columns_[8] = "prefix_len";
936  columns_[9] = "fqdn_fwd";
937  columns_[10] = "fqdn_rev";
938  columns_[11] = "hostname";
939  columns_[12] = "hwaddr";
940  columns_[13] = "hwtype";
941  columns_[14] = "hwaddr_source";
942  columns_[15] = "state";
943  columns_[16] = "user_context";
944  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
945  }
946 
955  std::vector<MYSQL_BIND> createBindForSend(const Lease6Ptr& lease) {
956  // Store lease object to ensure it remains valid.
957  lease_ = lease;
958 
959  // Ensure bind_ array clear for constructing the MYSQL_BIND structures
960  // for this lease.
961  // It sets all fields, including is_null, to zero, so we need to set
962  // is_null only if it should be true. This gives up minor performance
963  // benefit while being safe approach. For improved readability, the
964  // code that explicitly sets is_null is there, but is commented out.
965  memset(bind_, 0, sizeof(bind_));
966 
967  try {
968  // address: varchar(39)
969  addr6_ = lease_->addr_.toText();
970  addr6_length_ = addr6_.size();
971 
972  // In the following statement, the string is being read. However, the
973  // MySQL C interface does not use "const", so the "buffer" element
974  // is declared as "char*" instead of "const char*". To resolve this,
975  // the "const" is discarded. (Note that the address of addr6_.c_str()
976  // is guaranteed to be valid until the next non-const operation on
977  // addr6_.)
978  //
979  // The const_cast could be avoided by copying the string to a writable
980  // buffer and storing the address of that in the "buffer" element.
981  // However, this introduces a copy operation (with additional overhead)
982  // purely to get round the structures introduced by design of the
983  // MySQL interface (which uses the area pointed to by "buffer" as input
984  // when specifying query parameters and as output when retrieving data).
985  // For that reason, "const_cast" has been used.
986  bind_[0].buffer_type = MYSQL_TYPE_STRING;
987  bind_[0].buffer = const_cast<char*>(addr6_.c_str());
988  bind_[0].buffer_length = addr6_length_;
989  bind_[0].length = &addr6_length_;
990  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
991  // reasons, see memset() above
992 
993  // duid: varchar(128)
994  if (!lease_->duid_) {
995  isc_throw(DbOperationError, "lease6 for address " << addr6_
996  << " is missing mandatory client-id.");
997  }
998  duid_ = lease_->duid_->getDuid();
999  duid_length_ = duid_.size();
1000 
1001  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
1002  bind_[1].buffer = reinterpret_cast<char*>(&(duid_[0]));
1003  bind_[1].buffer_length = duid_length_;
1004  bind_[1].length = &duid_length_;
1005  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
1006  // reasons, see memset() above
1007 
1008  // valid lifetime: unsigned int
1009  bind_[2].buffer_type = MYSQL_TYPE_LONG;
1010  bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
1011  bind_[2].is_unsigned = MLM_TRUE;
1012  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
1013  // reasons, see memset() above
1014 
1015  // expire: timestamp
1016  // The lease structure holds the client last transmission time (cltt_)
1017  // For convenience for external tools, this is converted to lease
1018  // expiry time (expire). The relationship is given by:
1019  //
1020  // expire = cltt_ + valid_lft_
1021  // Avoid overflow with infinite valid lifetime by using
1022  // expire = cltt_ when valid_lft_ = 0xffffffff
1023  uint32_t valid_lft = lease_->valid_lft_;
1024  if (valid_lft == Lease::INFINITY_LFT) {
1025  valid_lft = 0;
1026  }
1027  MySqlConnection::convertToDatabaseTime(lease_->cltt_, valid_lft,
1028  expire_);
1029  bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
1030  bind_[3].buffer = reinterpret_cast<char*>(&expire_);
1031  bind_[3].buffer_length = sizeof(expire_);
1032  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
1033  // reasons, see memset() above
1034 
1035  // subnet_id: unsigned int
1036  // Can use lease_->subnet_id_ directly as it is of type uint32_t.
1037  bind_[4].buffer_type = MYSQL_TYPE_LONG;
1038  bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
1039  bind_[4].is_unsigned = MLM_TRUE;
1040  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
1041  // reasons, see memset() above
1042 
1043  // pref_lifetime: unsigned int
1044  // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
1045  bind_[5].buffer_type = MYSQL_TYPE_LONG;
1046  bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
1047  bind_[5].is_unsigned = MLM_TRUE;
1048  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
1049  // reasons, see memset() above
1050 
1051  // lease_type: tinyint
1052  // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
1053  lease_type_ = lease_->type_;
1054  bind_[6].buffer_type = MYSQL_TYPE_TINY;
1055  bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
1056  bind_[6].is_unsigned = MLM_TRUE;
1057  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
1058  // reasons, see memset() above
1059 
1060  // iaid: unsigned int
1061  // Can use lease_->iaid_ directly as it is of type uint32_t.
1062  bind_[7].buffer_type = MYSQL_TYPE_LONG;
1063  bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
1064  bind_[7].is_unsigned = MLM_TRUE;
1065  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1066  // reasons, see memset() above
1067 
1068  // prefix_len: unsigned tinyint
1069  // Can use lease_->prefixlen_ directly as it is uint32_t.
1070  bind_[8].buffer_type = MYSQL_TYPE_TINY;
1071  bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
1072  bind_[8].is_unsigned = MLM_TRUE;
1073  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
1074  // reasons, see memset() above
1075 
1076  // fqdn_fwd: boolean
1077  bind_[9].buffer_type = MYSQL_TYPE_TINY;
1078  bind_[9].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
1079  bind_[9].is_unsigned = MLM_TRUE;
1080  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1081  // reasons, see memset() above
1082 
1083  // fqdn_rev: boolean
1084  bind_[10].buffer_type = MYSQL_TYPE_TINY;
1085  bind_[10].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
1086  bind_[10].is_unsigned = MLM_TRUE;
1087  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
1088  // reasons, see memset() above
1089 
1090  // hostname: varchar(255)
1091  bind_[11].buffer_type = MYSQL_TYPE_STRING;
1092  bind_[11].buffer = const_cast<char*>(lease_->hostname_.c_str());
1093  bind_[11].buffer_length = lease_->hostname_.length();
1094  // bind_[11].is_null = &MLM_FALSE; // commented out for performance
1095  // reasons, see memset() above
1096 
1097  // hwaddr: varbinary(20) - hardware/MAC address
1098  HWAddrPtr hwaddr = lease_->hwaddr_;
1099  if (hwaddr) {
1100  hwaddr_ = hwaddr->hwaddr_;
1101  hwaddr_length_ = hwaddr->hwaddr_.size();
1102 
1103  // Make sure that the buffer has at least length of 1, even if
1104  // empty HW address is passed. This is required by some of the
1105  // MySQL connectors that the buffer is set to non-null value.
1106  // Otherwise, null value would be inserted into the database,
1107  // rather than empty string.
1108  if (hwaddr_.empty()) {
1109  hwaddr_.resize(1);
1110  }
1111 
1112  bind_[12].buffer_type = MYSQL_TYPE_BLOB;
1113  bind_[12].buffer = reinterpret_cast<char*>(&(hwaddr_[0]));
1114  bind_[12].buffer_length = hwaddr_length_;
1115  bind_[12].length = &hwaddr_length_;
1116  } else {
1117  bind_[12].buffer_type = MYSQL_TYPE_NULL;
1118  // According to http://dev.mysql.com/doc/refman/5.5/en/
1119  // c-api-prepared-statement-data-structures.html, the other
1120  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1121  // but let's set them to some sane values in case earlier versions
1122  // didn't have that assumption.
1123  hwaddr_null_ = MLM_TRUE;
1124  bind_[12].buffer = NULL;
1125  bind_[12].is_null = &hwaddr_null_;
1126  }
1127 
1128  // hardware type: unsigned short int (16 bits)
1129  if (hwaddr) {
1130  hwtype_ = lease->hwaddr_->htype_;
1131  bind_[13].buffer_type = MYSQL_TYPE_SHORT;
1132  bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
1133  bind_[13].is_unsigned = MLM_TRUE;
1134  } else {
1135  hwtype_ = 0;
1136  bind_[13].buffer_type = MYSQL_TYPE_NULL;
1137  // According to http://dev.mysql.com/doc/refman/5.5/en/
1138  // c-api-prepared-statement-data-structures.html, the other
1139  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1140  // but let's set them to some sane values in case earlier versions
1141  // didn't have that assumption.
1142  hwaddr_null_ = MLM_TRUE;
1143  bind_[13].buffer = NULL;
1144  bind_[13].is_null = &hwaddr_null_;
1145  }
1146 
1147  // hardware source: unsigned int (32 bits)
1148  if (hwaddr) {
1149  hwaddr_source_ = lease->hwaddr_->source_;
1150  bind_[14].buffer_type = MYSQL_TYPE_LONG;
1151  bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
1152  bind_[14].is_unsigned = MLM_TRUE;
1153  } else {
1154  hwaddr_source_ = 0;
1155  bind_[14].buffer_type = MYSQL_TYPE_NULL;
1156  // According to http://dev.mysql.com/doc/refman/5.5/en/
1157  // c-api-prepared-statement-data-structures.html, the other
1158  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1159  // but let's set them to some sane values in case earlier versions
1160  // didn't have that assumption.
1161  hwaddr_null_ = MLM_TRUE;
1162  bind_[14].buffer = NULL;
1163  bind_[14].is_null = &hwaddr_null_;
1164  }
1165 
1166  // state: uint32_t
1167  bind_[15].buffer_type = MYSQL_TYPE_LONG;
1168  bind_[15].buffer = reinterpret_cast<char*>(&lease_->state_);
1169  bind_[15].is_unsigned = MLM_TRUE;
1170  // bind_[15].is_null = &MLM_FALSE; // commented out for performance
1171  // reasons, see memset() above
1172 
1173  // user_context: text
1174  ConstElementPtr ctx = lease->getContext();
1175  if (ctx) {
1176  bind_[16].buffer_type = MYSQL_TYPE_STRING;
1177  std::string ctx_txt = ctx->str();
1178  strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
1179  bind_[16].buffer = user_context_;
1180  bind_[16].buffer_length = ctx_txt.length();
1181  // bind_[16].is_null = &MLM_FALSE; // commented out for performance
1182  // reasons, see memset() above
1183  } else {
1184  bind_[16].buffer_type = MYSQL_TYPE_NULL;
1185  }
1186 
1187  // Add the error flags
1188  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
1189 
1190  // .. and check that we have the numbers correct at compile time.
1191  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
1192 
1193  } catch (const std::exception& ex) {
1195  "Could not create bind array from Lease6: "
1196  << lease_->addr_.toText() << ", reason: " << ex.what());
1197  }
1198 
1199  // Add the data to the vector. Note the end element is one after the
1200  // end of the array.
1201  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
1202  }
1203 
1212  std::vector<MYSQL_BIND> createBindForReceive() {
1213 
1214  // Initialize MYSQL_BIND array.
1215  // It sets all fields, including is_null, to zero, so we need to set
1216  // is_null only if it should be true. This gives up minor performance
1217  // benefit while being safe approach. For improved readability, the
1218  // code that explicitly sets is_null is there, but is commented out.
1219  memset(bind_, 0, sizeof(bind_));
1220 
1221  // address: varchar(39)
1222  // A Lease6_ address has a maximum of 39 characters. The array is
1223  // one byte longer than this to guarantee that we can always null
1224  // terminate it whatever is returned.
1225  addr6_length_ = sizeof(addr6_buffer_) - 1;
1226  bind_[0].buffer_type = MYSQL_TYPE_STRING;
1227  bind_[0].buffer = addr6_buffer_;
1228  bind_[0].buffer_length = addr6_length_;
1229  bind_[0].length = &addr6_length_;
1230  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
1231  // reasons, see memset() above
1232 
1233  // client_id: varbinary(128)
1234  duid_length_ = sizeof(duid_buffer_);
1235  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
1236  bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
1237  bind_[1].buffer_length = duid_length_;
1238  bind_[1].length = &duid_length_;
1239  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
1240  // reasons, see memset() above
1241 
1242  // valid lifetime: unsigned int
1243  bind_[2].buffer_type = MYSQL_TYPE_LONG;
1244  bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
1245  bind_[2].is_unsigned = MLM_TRUE;
1246  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
1247  // reasons, see memset() above
1248 
1249  // expire: timestamp
1250  bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
1251  bind_[3].buffer = reinterpret_cast<char*>(&expire_);
1252  bind_[3].buffer_length = sizeof(expire_);
1253  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
1254  // reasons, see memset() above
1255 
1256  // subnet_id: unsigned int
1257  bind_[4].buffer_type = MYSQL_TYPE_LONG;
1258  bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
1259  bind_[4].is_unsigned = MLM_TRUE;
1260  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
1261  // reasons, see memset() above
1262 
1263  // pref_lifetime: unsigned int
1264  bind_[5].buffer_type = MYSQL_TYPE_LONG;
1265  bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
1266  bind_[5].is_unsigned = MLM_TRUE;
1267  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
1268  // reasons, see memset() above
1269 
1270  // lease_type: tinyint
1271  bind_[6].buffer_type = MYSQL_TYPE_TINY;
1272  bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
1273  bind_[6].is_unsigned = MLM_TRUE;
1274  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
1275  // reasons, see memset() above
1276 
1277  // iaid: unsigned int
1278  bind_[7].buffer_type = MYSQL_TYPE_LONG;
1279  bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
1280  bind_[7].is_unsigned = MLM_TRUE;
1281  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1282  // reasons, see memset() above
1283 
1284  // prefix_len: unsigned tinyint
1285  bind_[8].buffer_type = MYSQL_TYPE_TINY;
1286  bind_[8].buffer = reinterpret_cast<char*>(&prefix_len_);
1287  bind_[8].is_unsigned = MLM_TRUE;
1288  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
1289  // reasons, see memset() above
1290 
1291  // fqdn_fwd: boolean
1292  bind_[9].buffer_type = MYSQL_TYPE_TINY;
1293  bind_[9].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
1294  bind_[9].is_unsigned = MLM_TRUE;
1295  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
1296  // reasons, see memset() above
1297 
1298  // fqdn_rev: boolean
1299  bind_[10].buffer_type = MYSQL_TYPE_TINY;
1300  bind_[10].buffer = reinterpret_cast<char*>(&fqdn_rev_);
1301  bind_[10].is_unsigned = MLM_TRUE;
1302  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
1303  // reasons, see memset() above
1304 
1305  // hostname: varchar(255)
1306  hostname_length_ = sizeof(hostname_buffer_);
1307  bind_[11].buffer_type = MYSQL_TYPE_STRING;
1308  bind_[11].buffer = reinterpret_cast<char*>(hostname_buffer_);
1309  bind_[11].buffer_length = hostname_length_;
1310  bind_[11].length = &hostname_length_;
1311  // bind_[11].is_null = &MLM_FALSE; // commented out for performance
1312  // reasons, see memset() above
1313 
1314  // hwaddr: varbinary(20)
1315  hwaddr_null_ = MLM_FALSE;
1316  hwaddr_length_ = sizeof(hwaddr_buffer_);
1317  bind_[12].buffer_type = MYSQL_TYPE_BLOB;
1318  bind_[12].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
1319  bind_[12].buffer_length = hwaddr_length_;
1320  bind_[12].length = &hwaddr_length_;
1321  bind_[12].is_null = &hwaddr_null_;
1322 
1323  // hardware type: unsigned short int (16 bits)
1324  bind_[13].buffer_type = MYSQL_TYPE_SHORT;
1325  bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
1326  bind_[13].is_unsigned = MLM_TRUE;
1327 
1328  // hardware source: unsigned int (32 bits)
1329  bind_[14].buffer_type = MYSQL_TYPE_LONG;
1330  bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
1331  bind_[14].is_unsigned = MLM_TRUE;
1332 
1333  // state: uint32_t
1334  bind_[15].buffer_type = MYSQL_TYPE_LONG;
1335  bind_[15].buffer = reinterpret_cast<char*>(&state_);
1336  bind_[15].is_unsigned = MLM_TRUE;
1337  // bind_[15].is_null = &MLM_FALSE; // commented out for performance
1338  // reasons, see memset() above
1339 
1340  // user_context: text
1341  user_context_null_ = MLM_FALSE;
1342  user_context_length_ = sizeof(user_context_);
1343  bind_[16].buffer_type = MYSQL_TYPE_STRING;
1344  bind_[16].buffer = reinterpret_cast<char*>(user_context_);
1345  bind_[16].buffer_length = user_context_length_;
1346  bind_[16].length = &user_context_length_;
1347  bind_[16].is_null = &user_context_null_;
1348 
1349  // Add the error flags
1350  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
1351 
1352  // .. and check that we have the numbers correct at compile time.
1353  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
1354 
1355  // Add the data to the vector. Note the end element is one after the
1356  // end of the array.
1357  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
1358  }
1359 
1371  // The address buffer is declared larger than the buffer size passed
1372  // to the access function so that we can always append a null byte.
1373  // Create the IOAddress object corresponding to the received data.
1374  addr6_buffer_[addr6_length_] = '\0';
1375  std::string address = addr6_buffer_;
1376  IOAddress addr(address);
1377 
1378  // Set the lease type in a variable of the appropriate data type, which
1379  // has been initialized with an arbitrary (but valid) value.
1380  Lease::Type type = Lease::TYPE_NA;
1381  switch (lease_type_) {
1382  case Lease::TYPE_NA:
1383  type = Lease::TYPE_NA;
1384  break;
1385 
1386  case Lease::TYPE_TA:
1387  type = Lease::TYPE_TA;
1388  break;
1389 
1390  case Lease::TYPE_PD:
1391  type = Lease::TYPE_PD;
1392  break;
1393 
1394  default:
1395  isc_throw(BadValue, "invalid lease type returned (" <<
1396  static_cast<int>(lease_type_) << ") for lease with "
1397  << "address " << address << ". Only 0, 1, or 2 are "
1398  << "allowed.");
1399  }
1400 
1401  // Set up DUID,
1402  DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
1403 
1404  // Hostname is passed to Lease6 as a string object, so we have to
1405  // create it from the hostname buffer and length.
1406  std::string hostname(hostname_buffer_,
1407  hostname_buffer_ + hostname_length_);
1408 
1409  // Set hardware address if it was set
1410  HWAddrPtr hwaddr;
1411  if (hwaddr_null_ == MLM_FALSE) {
1412  hwaddr.reset(new HWAddr(hwaddr_buffer_, hwaddr_length_, hwtype_));
1413  hwaddr->source_ = hwaddr_source_;
1414  }
1415 
1416  // Convert user_context to string as well.
1417  std::string user_context;
1418  if (user_context_null_ == MLM_FALSE) {
1419  user_context_[user_context_length_] = '\0';
1420  user_context.assign(user_context_);
1421  }
1422 
1423  // Set the user context if there is one.
1424  ConstElementPtr ctx;
1425  if (!user_context.empty()) {
1426  ctx = Element::fromJSON(user_context);
1427  if (!ctx || (ctx->getType() != Element::map)) {
1428  isc_throw(BadValue, "user context '" << user_context
1429  << "' is not a JSON map");
1430  }
1431  }
1432 
1433  // Create the lease and set the cltt (after converting from the
1434  // expire time retrieved from the database).
1435  Lease6Ptr result(boost::make_shared<Lease6>(type, addr, duid_ptr, iaid_,
1436  pref_lifetime_,
1437  valid_lifetime_, subnet_id_,
1438  fqdn_fwd_, fqdn_rev_,
1439  hostname, hwaddr,
1440  prefix_len_));
1441  time_t cltt = 0;
1442  // Recover from overflow (see expire code of createBindForSend).
1443  uint32_t valid_lft = valid_lifetime_;
1444  if (valid_lft == Lease::INFINITY_LFT) {
1445  valid_lft = 0;
1446  }
1447  MySqlConnection::convertFromDatabaseTime(expire_, valid_lft, cltt);
1448  // Update cltt_ and current_cltt_ explicitly.
1449  result->cltt_ = cltt;
1450  result->current_cltt_ = cltt;
1451 
1452  // Set state.
1453  result->state_ = state_;
1454 
1455  if (ctx) {
1456  result->setContext(ctx);
1457  }
1458 
1459  return (result);
1460  }
1461 
1472  std::string getErrorColumns() {
1473  return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
1474  }
1475 
1476 private:
1477 
1478  // Note: All array lengths are equal to the corresponding variable in the
1479  // schema.
1480  // Note: arrays are declared fixed length for speed of creation
1481  std::string addr6_;
1482  char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];
1483  unsigned long addr6_length_;
1484  MYSQL_BIND bind_[LEASE_COLUMNS];
1485  std::string columns_[LEASE_COLUMNS];
1486  my_bool error_[LEASE_COLUMNS];
1487  Lease6Ptr lease_;
1488  std::vector<uint8_t> hwaddr_;
1489  uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
1490  unsigned long hwaddr_length_;
1491  my_bool hwaddr_null_;
1492  std::vector<uint8_t> duid_;
1493  uint8_t duid_buffer_[DUID::MAX_DUID_LEN];
1494  unsigned long duid_length_;
1495  MYSQL_TIME expire_;
1496  uint32_t iaid_;
1497  uint8_t lease_type_;
1498  uint8_t prefix_len_;
1499  uint32_t pref_lifetime_;
1500  uint32_t subnet_id_;
1501  uint32_t valid_lifetime_;
1502  my_bool fqdn_fwd_;
1503  my_bool fqdn_rev_;
1504  char hostname_buffer_[HOSTNAME_MAX_LEN];
1505  unsigned long hostname_length_;
1506  uint16_t hwtype_;
1507  uint32_t hwaddr_source_;
1508  uint32_t state_;
1509  char user_context_[USER_CONTEXT_MAX_LEN];
1510  unsigned long user_context_length_;
1511  my_bool user_context_null_;
1512 };
1513 
1520 
1522 public:
1523 
1532  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1533  const bool fetch_type)
1534  : conn_(conn), statement_index_(statement_index), statement_(NULL),
1535  fetch_type_(fetch_type),
1536  // Set the number of columns in the bind array based on fetch_type
1537  // This is the number of columns expected in the result set
1538  bind_(fetch_type_ ? 4 : 3),
1539  subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
1540  validateStatement();
1541  }
1542 
1552  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1553  const bool fetch_type, const SubnetID& subnet_id)
1554  : LeaseStatsQuery(subnet_id), conn_(conn), statement_index_(statement_index),
1555  statement_(NULL), fetch_type_(fetch_type),
1556  // Set the number of columns in the bind array based on fetch_type
1557  // This is the number of columns expected in the result set
1558  bind_(fetch_type_ ? 4 : 3),
1559  subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
1560  validateStatement();
1561  }
1562 
1575  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1576  const bool fetch_type, const SubnetID& first_subnet_id,
1577  const SubnetID& last_subnet_id)
1578  : LeaseStatsQuery(first_subnet_id, last_subnet_id), conn_(conn),
1579  statement_index_(statement_index), statement_(NULL), fetch_type_(fetch_type),
1580  // Set the number of columns in the bind array based on fetch_type
1581  // This is the number of columns expected in the result set
1582  bind_(fetch_type_ ? 4 : 3),
1583  subnet_id_(0), lease_type_(0), state_(0), state_count_(0) {
1584  validateStatement();
1585  }
1586 
1589  (void) mysql_stmt_free_result(statement_);
1590  }
1591 
1599  void start() {
1600  // Set up where clause inputs if needed.
1601  if (getSelectMode() != ALL_SUBNETS) {
1602  MYSQL_BIND inbind[2];
1603  memset(inbind, 0, sizeof(inbind));
1604 
1605  // Add first_subnet_id used by both single and range.
1606  inbind[0].buffer_type = MYSQL_TYPE_LONG;
1607  inbind[0].buffer = reinterpret_cast<char*>(&first_subnet_id_);
1608  inbind[0].is_unsigned = MLM_TRUE;
1609 
1610  // Add last_subnet_id for range.
1611  if (getSelectMode() == SUBNET_RANGE) {
1612  inbind[1].buffer_type = MYSQL_TYPE_LONG;
1613  inbind[1].buffer = reinterpret_cast<char*>(&last_subnet_id_);
1614  inbind[1].is_unsigned = MLM_TRUE;
1615  }
1616 
1617  // Bind the parameters to the statement
1618  int status = mysql_stmt_bind_param(statement_, &inbind[0]);
1619  conn_.checkError(status, statement_index_, "unable to bind parameters");
1620  }
1621 
1622  int col = 0;
1623  // subnet_id: unsigned int
1624  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1625  bind_[col].buffer = reinterpret_cast<char*>(&subnet_id_);
1626  bind_[col].is_unsigned = MLM_TRUE;
1627  ++col;
1628 
1629  // Fetch the lease type if we were told to do so.
1630  if (fetch_type_) {
1631  // lease type: uint32_t
1632  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1633  bind_[col].buffer = reinterpret_cast<char*>(&lease_type_);
1634  bind_[col].is_unsigned = MLM_TRUE;
1635  ++col;
1636  } else {
1637  fetch_type_ = Lease::TYPE_NA;
1638  }
1639 
1640  // state: uint32_t
1641  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1642  bind_[col].buffer = reinterpret_cast<char*>(&state_);
1643  bind_[col].is_unsigned = MLM_TRUE;
1644  ++col;
1645 
1646  // state_count_: int64_t
1647  bind_[col].buffer_type = MYSQL_TYPE_LONGLONG;
1648  bind_[col].buffer = reinterpret_cast<char*>(&state_count_);
1649  //bind_[col].is_unsigned = MLM_FALSE;
1650 
1651  // Set up the MYSQL_BIND array for the data being returned
1652  // and bind it to the statement.
1653  int status = mysql_stmt_bind_result(statement_, &bind_[0]);
1654  conn_.checkError(status, statement_index_, "outbound binding failed");
1655 
1656  // Execute the statement
1657  status = MysqlExecuteStatement(statement_);
1658  conn_.checkError(status, statement_index_, "unable to execute");
1659 
1660  // Ensure that all the lease information is retrieved in one go to avoid
1661  // overhead of going back and forth between client and server.
1662  status = mysql_stmt_store_result(statement_);
1663  conn_.checkError(status, statement_index_, "results storage failed");
1664  }
1665 
1682  bool have_row = false;
1683  int status = mysql_stmt_fetch(statement_);
1684  if (status == MLM_MYSQL_FETCH_SUCCESS) {
1685  row.subnet_id_ = static_cast<SubnetID>(subnet_id_);
1686  row.lease_type_ = static_cast<Lease::Type>(lease_type_);
1687  row.lease_state_ = state_;
1688  if (state_count_ >= 0) {
1689  row.state_count_ = state_count_;
1690  } else {
1691  row.state_count_ = 0;
1692  if (!negative_count_) {
1693  negative_count_ = true;
1695  }
1696  }
1697  have_row = true;
1698  } else if (status != MYSQL_NO_DATA) {
1699  conn_.checkError(status, statement_index_, "getNextRow failed");
1700  }
1701 
1702  return (have_row);
1703  }
1704 
1705 private:
1706 
1710  void validateStatement() {
1711  if (statement_index_ >= MySqlLeaseMgr::NUM_STATEMENTS) {
1712  isc_throw(BadValue, "MySqlLeaseStatsQuery"
1713  " - invalid statement index" << statement_index_);
1714  }
1715 
1716  statement_ = conn_.statements_[statement_index_];
1717  }
1718 
1720  MySqlConnection& conn_;
1721 
1723  size_t statement_index_;
1724 
1726  MYSQL_STMT *statement_;
1727 
1729  bool fetch_type_;
1730 
1732  std::vector<MYSQL_BIND> bind_;
1733 
1735  uint32_t subnet_id_;
1736 
1738  uint32_t lease_type_;
1739 
1741  uint32_t state_;
1742 
1744  int64_t state_count_;
1745 
1747  static bool negative_count_;
1748 };
1749 
1750 // Initialize negative state count flag to false.
1751 bool MySqlLeaseStatsQuery::negative_count_ = false;
1752 
1753 // MySqlLeaseContext Constructor
1754 
1755 MySqlLeaseContext::MySqlLeaseContext(const DatabaseConnection::ParameterMap& parameters,
1756  IOServiceAccessorPtr io_service_accessor,
1757  DbCallback db_reconnect_callback)
1758  : conn_(parameters, io_service_accessor, db_reconnect_callback) {
1759 }
1760 
1761 // MySqlLeaseContextAlloc Constructor and Destructor
1762 
1763 MySqlLeaseMgr::MySqlLeaseContextAlloc::MySqlLeaseContextAlloc(
1764  const MySqlLeaseMgr& mgr) : ctx_(), mgr_(mgr) {
1765 
1766  if (MultiThreadingMgr::instance().getMode()) {
1767  // multi-threaded
1768  {
1769  // we need to protect the whole pool_ operation, hence extra scope {}
1770  lock_guard<mutex> lock(mgr_.pool_->mutex_);
1771  if (!mgr_.pool_->pool_.empty()) {
1772  ctx_ = mgr_.pool_->pool_.back();
1773  mgr_.pool_->pool_.pop_back();
1774  }
1775  }
1776  if (!ctx_) {
1777  ctx_ = mgr_.createContext();
1778  }
1779  } else {
1780  // single-threaded
1781  if (mgr_.pool_->pool_.empty()) {
1782  isc_throw(Unexpected, "No available MySQL lease context?!");
1783  }
1784  ctx_ = mgr_.pool_->pool_.back();
1785  }
1786 }
1787 
1788 MySqlLeaseMgr::MySqlLeaseContextAlloc::~MySqlLeaseContextAlloc() {
1789  if (MultiThreadingMgr::instance().getMode()) {
1790  // multi-threaded
1791  lock_guard<mutex> lock(mgr_.pool_->mutex_);
1792  mgr_.pool_->pool_.push_back(ctx_);
1793  }
1794  // If running in single-threaded mode, there's nothing to do here.
1795 }
1796 
1797 // MySqlLeaseMgr Constructor and Destructor
1798 
1800  : parameters_(parameters), timer_name_("") {
1801 
1802  // Create unique timer name per instance.
1803  timer_name_ = "MySqlLeaseMgr[";
1804  timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
1805  timer_name_ += "]DbReconnectTimer";
1806 
1807  // Validate schema version first.
1808  std::pair<uint32_t, uint32_t> code_version(MYSQL_SCHEMA_VERSION_MAJOR,
1810  std::pair<uint32_t, uint32_t> db_version = getVersion();
1811  if (code_version != db_version) {
1813  "MySQL schema version mismatch: need version: "
1814  << code_version.first << "." << code_version.second
1815  << " found version: " << db_version.first << "."
1816  << db_version.second);
1817  }
1818 
1819  // Create an initial context.
1820  pool_.reset(new MySqlLeaseContextPool());
1821  pool_->pool_.push_back(createContext());
1822 }
1823 
1825 }
1826 
1827 bool
1830 
1831  // Invoke application layer connection lost callback.
1832  if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
1833  return (false);
1834  }
1835 
1836  bool reopened = false;
1837 
1838  const std::string timer_name = db_reconnect_ctl->timerName();
1839 
1840  // At least one connection was lost.
1841  try {
1842  CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
1844  LeaseMgrFactory::create(cfg_db->getLeaseDbAccessString());
1845  reopened = true;
1846  } catch (const std::exception& ex) {
1848  .arg(ex.what());
1849  }
1850 
1851  if (reopened) {
1852  // Cancel the timer.
1853  if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
1854  TimerMgr::instance()->unregisterTimer(timer_name);
1855  }
1856 
1857  // Invoke application layer connection recovered callback.
1858  if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
1859  return (false);
1860  }
1861  } else {
1862  if (!db_reconnect_ctl->checkRetries()) {
1863  // We're out of retries, log it and initiate shutdown.
1865  .arg(db_reconnect_ctl->maxRetries());
1866 
1867  // Cancel the timer.
1868  if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
1869  TimerMgr::instance()->unregisterTimer(timer_name);
1870  }
1871 
1872  // Invoke application layer connection failed callback.
1874  return (false);
1875  }
1876 
1878  .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
1879  .arg(db_reconnect_ctl->maxRetries())
1880  .arg(db_reconnect_ctl->retryInterval());
1881 
1882  // Start the timer.
1883  if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
1884  TimerMgr::instance()->registerTimer(timer_name,
1885  std::bind(&MySqlLeaseMgr::dbReconnect, db_reconnect_ctl),
1886  db_reconnect_ctl->retryInterval(),
1888  }
1889  TimerMgr::instance()->setup(timer_name);
1890  }
1891 
1892  return (true);
1893 }
1894 
1895 // Create context.
1896 
1899  MySqlLeaseContextPtr ctx(new MySqlLeaseContext(parameters_,
1902 
1903  // Open the database.
1904  ctx->conn_.openDatabase();
1905 
1906  // Check if we have TLS when we required it.
1907  if (ctx->conn_.getTls()) {
1908  std::string cipher = ctx->conn_.getTlsCipher();
1909  if (cipher.empty()) {
1911  } else {
1914  .arg(cipher);
1915  }
1916  }
1917 
1918  // Prepare all statements likely to be used.
1919  ctx->conn_.prepareStatements(tagged_statements.begin(),
1920  tagged_statements.end());
1921 
1922  // Create the exchange objects for use in exchanging data between the
1923  // program and the database.
1924  ctx->exchange4_.reset(new MySqlLease4Exchange());
1925  ctx->exchange6_.reset(new MySqlLease6Exchange());
1926 
1927  // Create ReconnectCtl for this connection.
1928  ctx->conn_.makeReconnectCtl(timer_name_);
1929 
1930  return (ctx);
1931 }
1932 
1933 std::string
1935  std::stringstream tmp;
1936  tmp << "MySQL backend " << MYSQL_SCHEMA_VERSION_MAJOR;
1937  tmp << "." << MYSQL_SCHEMA_VERSION_MINOR;
1938  tmp << ", library " << mysql_get_client_info();
1939  return (tmp.str());
1940 }
1941 
1942 // Add leases to the database. The two public methods accept a lease object
1943 // (either V4 of V6), bind the contents to the appropriate prepared
1944 // statement, then call common code to execute the statement.
1945 
1946 bool
1947 MySqlLeaseMgr::addLeaseCommon(MySqlLeaseContextPtr& ctx,
1948  StatementIndex stindex,
1949  std::vector<MYSQL_BIND>& bind) {
1950 
1951  // Bind the parameters to the statement
1952  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], &bind[0]);
1953  checkError(ctx, status, stindex, "unable to bind parameters");
1954 
1955  // Execute the statement
1956  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
1957  if (status != 0) {
1958 
1959  // Failure: check for the special case of duplicate entry. If this is
1960  // the case, we return false to indicate that the row was not added.
1961  // Otherwise we throw an exception.
1962  if (mysql_errno(ctx->conn_.mysql_) == ER_DUP_ENTRY) {
1963  return (false);
1964  }
1965  checkError(ctx, status, stindex, "unable to execute");
1966  }
1967 
1968  // Insert succeeded
1969  return (true);
1970 }
1971 
1972 bool
1975  .arg(lease->addr_.toText());
1976 
1977  // Get a context
1978  MySqlLeaseContextAlloc get_context(*this);
1979  MySqlLeaseContextPtr ctx = get_context.ctx_;
1980 
1981  // Create the MYSQL_BIND array for the lease
1982  std::vector<MYSQL_BIND> bind = ctx->exchange4_->createBindForSend(lease);
1983 
1984  // ... and drop to common code.
1985  auto result = addLeaseCommon(ctx, INSERT_LEASE4, bind);
1986 
1987  // Update lease current expiration time (allows update between the creation
1988  // of the Lease up to the point of insertion in the database).
1989  lease->updateCurrentExpirationTime();
1990 
1991  return (result);
1992 }
1993 
1994 bool
1997  .arg(lease->addr_.toText())
1998  .arg(lease->type_);
1999 
2000  // Get a context
2001  MySqlLeaseContextAlloc get_context(*this);
2002  MySqlLeaseContextPtr ctx = get_context.ctx_;
2003 
2004  // Create the MYSQL_BIND array for the lease
2005  std::vector<MYSQL_BIND> bind = ctx->exchange6_->createBindForSend(lease);
2006 
2007  // ... and drop to common code.
2008  auto result = addLeaseCommon(ctx, INSERT_LEASE6, bind);
2009 
2010  // Update lease current expiration time (allows update between the creation
2011  // of the Lease up to the point of insertion in the database).
2012  lease->updateCurrentExpirationTime();
2013 
2014  return (result);
2015 }
2016 
2017 // Extraction of leases from the database.
2018 //
2019 // All getLease() methods ultimately call getLeaseCollection(). This
2020 // binds the input parameters passed to it with the appropriate prepared
2021 // statement and executes the statement. It then gets the results from the
2022 // database. getlease() methods that expect a single result back call it
2023 // with the "single" parameter set true: this causes an exception to be
2024 // generated if multiple records can be retrieved from the result set. (Such
2025 // an occurrence either indicates corruption in the database, or that an
2026 // assumption that a query can only return a single record is incorrect.)
2027 // Methods that require a collection of records have "single" set to the
2028 // default value of false. The logic is the same for both Lease4 and Lease6
2029 // objects, so the code is templated.
2030 //
2031 // Methods that require a collection of objects access this method through
2032 // two interface methods (also called getLeaseCollection()). These are
2033 // short enough as to be defined in the header file: all they do is to supply
2034 // the appropriate MySqlLeaseXExchange object depending on the type of the
2035 // LeaseCollection objects passed to them.
2036 //
2037 // Methods that require a single object to be returned access the method
2038 // through two interface methods (called getLease()). As well as supplying
2039 // the appropriate exchange object, they convert between lease collection
2040 // holding zero or one leases into an appropriate Lease object.
2041 
2042 template <typename Exchange, typename LeaseCollection>
2043 void
2044 MySqlLeaseMgr::getLeaseCollection(MySqlLeaseContextPtr& ctx,
2045  StatementIndex stindex,
2046  MYSQL_BIND* bind,
2047  Exchange& exchange,
2048  LeaseCollection& result,
2049  bool single) const {
2050  int status;
2051 
2052  if (bind) {
2053  // Bind the selection parameters to the statement
2054  status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], bind);
2055  checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
2056  }
2057 
2058  // Set up the MYSQL_BIND array for the data being returned and bind it to
2059  // the statement.
2060  std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
2061  status = mysql_stmt_bind_result(ctx->conn_.statements_[stindex], &outbind[0]);
2062  checkError(ctx, status, stindex, "unable to bind SELECT clause parameters");
2063 
2064  // Execute the statement
2065  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2066  checkError(ctx, status, stindex, "unable to execute");
2067 
2068  // Ensure that all the lease information is retrieved in one go to avoid
2069  // overhead of going back and forth between client and server.
2070  status = mysql_stmt_store_result(ctx->conn_.statements_[stindex]);
2071  checkError(ctx, status, stindex, "unable to set up for storing all results");
2072 
2073  // Set up the fetch "release" object to release resources associated
2074  // with the call to mysql_stmt_fetch when this method exits, then
2075  // retrieve the data.
2076  MySqlFreeResult fetch_release(ctx->conn_.statements_[stindex]);
2077  int count = 0;
2078  while ((status = mysql_stmt_fetch(ctx->conn_.statements_[stindex])) == 0) {
2079  try {
2080  result.push_back(exchange->getLeaseData());
2081 
2082  } catch (const isc::BadValue& ex) {
2083  // Rethrow the exception with a bit more data.
2084  isc_throw(BadValue, ex.what() << ". Statement is <" <<
2085  ctx->conn_.text_statements_[stindex] << ">");
2086  }
2087 
2088  if (single && (++count > 1)) {
2089  isc_throw(MultipleRecords, "multiple records were found in the "
2090  "database where only one was expected for query "
2091  << ctx->conn_.text_statements_[stindex]);
2092  }
2093  }
2094 
2095  // How did the fetch end?
2096  if (status == 1) {
2097  // Error - unable to fetch results
2098  checkError(ctx, status, stindex, "unable to fetch results");
2099  } else if (status == MYSQL_DATA_TRUNCATED) {
2100  // Data truncated - throw an exception indicating what was at fault
2101  isc_throw(DataTruncated, ctx->conn_.text_statements_[stindex]
2102  << " returned truncated data: columns affected are "
2103  << exchange->getErrorColumns());
2104  }
2105 }
2106 
2107 void
2108 MySqlLeaseMgr::getLease(MySqlLeaseContextPtr& ctx,
2109  StatementIndex stindex, MYSQL_BIND* bind,
2110  Lease4Ptr& result) const {
2111  // Create appropriate collection object and get all leases matching
2112  // the selection criteria. The "single" parameter is true to indicate
2113  // that the called method should throw an exception if multiple
2114  // matching records are found: this particular method is called when only
2115  // one or zero matches is expected.
2116  Lease4Collection collection;
2117  getLeaseCollection(ctx, stindex, bind, ctx->exchange4_, collection, true);
2118 
2119  // Return single record if present, else clear the lease.
2120  if (collection.empty()) {
2121  result.reset();
2122  } else {
2123  result = *collection.begin();
2124  }
2125 }
2126 
2127 void
2128 MySqlLeaseMgr::getLease(MySqlLeaseContextPtr& ctx,
2129  StatementIndex stindex, MYSQL_BIND* bind,
2130  Lease6Ptr& result) const {
2131  // Create appropriate collection object and get all leases matching
2132  // the selection criteria. The "single" parameter is true to indicate
2133  // that the called method should throw an exception if multiple
2134  // matching records are found: this particular method is called when only
2135  // one or zero matches is expected.
2136  Lease6Collection collection;
2137  getLeaseCollection(ctx, stindex, bind, ctx->exchange6_, collection, true);
2138 
2139  // Return single record if present, else clear the lease.
2140  if (collection.empty()) {
2141  result.reset();
2142  } else {
2143  result = *collection.begin();
2144  }
2145 }
2146 
2147 // Basic lease access methods. Obtain leases from the database using various
2148 // criteria.
2149 
2150 Lease4Ptr
2153  .arg(addr.toText());
2154 
2155  // Set up the WHERE clause value
2156  MYSQL_BIND inbind[1];
2157  memset(inbind, 0, sizeof(inbind));
2158 
2159  uint32_t addr4 = addr.toUint32();
2160  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2161  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
2162  inbind[0].is_unsigned = MLM_TRUE;
2163 
2164  // Get the data
2165  Lease4Ptr result;
2166 
2167  // Get a context
2168  MySqlLeaseContextAlloc get_context(*this);
2169  MySqlLeaseContextPtr ctx = get_context.ctx_;
2170 
2171  getLease(ctx, GET_LEASE4_ADDR, inbind, result);
2172 
2173  return (result);
2174 }
2175 
2177 MySqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
2179  .arg(hwaddr.toText());
2180 
2181  // Set up the WHERE clause value
2182  MYSQL_BIND inbind[1];
2183  memset(inbind, 0, sizeof(inbind));
2184 
2185  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2186 
2187  unsigned long hwaddr_length = hwaddr.hwaddr_.size();
2188 
2189  // If the data happens to be empty, we have to create a 1 byte dummy
2190  // buffer and pass it to the binding.
2191  uint8_t single_byte_data = 0;
2192 
2193  // As "buffer" is "char*" - even though the data is being read - we need
2194  // to cast away the "const"ness as well as reinterpreting the data as
2195  // a "char*". (We could avoid the "const_cast" by copying the data to a
2196  // local variable, but as the data is only being read, this introduces
2197  // an unnecessary copy).
2198  uint8_t* data = !hwaddr.hwaddr_.empty() ? const_cast<uint8_t*>(&hwaddr.hwaddr_[0])
2199  : &single_byte_data;
2200 
2201  inbind[0].buffer = reinterpret_cast<char*>(data);
2202  inbind[0].buffer_length = hwaddr_length;
2203  inbind[0].length = &hwaddr_length;
2204 
2205  // Get the data
2206  Lease4Collection result;
2207 
2208  // Get a context
2209  MySqlLeaseContextAlloc get_context(*this);
2210  MySqlLeaseContextPtr ctx = get_context.ctx_;
2211 
2212  getLeaseCollection(ctx, GET_LEASE4_HWADDR, inbind, result);
2213 
2214  return (result);
2215 }
2216 
2217 Lease4Ptr
2218 MySqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
2220  .arg(subnet_id)
2221  .arg(hwaddr.toText());
2222 
2223  // Set up the WHERE clause value
2224  MYSQL_BIND inbind[2];
2225  memset(inbind, 0, sizeof(inbind));
2226 
2227  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2228 
2229  unsigned long hwaddr_length = hwaddr.hwaddr_.size();
2230 
2231  // If the data happens to be empty, we have to create a 1 byte dummy
2232  // buffer and pass it to the binding.
2233  std::vector<uint8_t> single_byte_vec(1);
2234 
2235  // As "buffer" is "char*" - even though the data is being read - we need
2236  // to cast away the "const"ness as well as reinterpreting the data as
2237  // a "char*". (We could avoid the "const_cast" by copying the data to a
2238  // local variable, but as the data is only being read, this introduces
2239  // an unnecessary copy).
2240  uint8_t* data = !hwaddr.hwaddr_.empty() ? const_cast<uint8_t*>(&hwaddr.hwaddr_[0])
2241  : &single_byte_vec[0];
2242 
2243  inbind[0].buffer = reinterpret_cast<char*>(data);
2244  inbind[0].buffer_length = hwaddr_length;
2245  inbind[0].length = &hwaddr_length;
2246 
2247  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2248  inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
2249  inbind[1].is_unsigned = MLM_TRUE;
2250 
2251  // Get the data
2252  Lease4Ptr result;
2253 
2254  // Get a context
2255  MySqlLeaseContextAlloc get_context(*this);
2256  MySqlLeaseContextPtr ctx = get_context.ctx_;
2257 
2258  getLease(ctx, GET_LEASE4_HWADDR_SUBID, inbind, result);
2259 
2260  return (result);
2261 }
2262 
2264 MySqlLeaseMgr::getLease4(const ClientId& clientid) const {
2266  .arg(clientid.toText());
2267 
2268  // Set up the WHERE clause value
2269  MYSQL_BIND inbind[1];
2270  memset(inbind, 0, sizeof(inbind));
2271 
2272  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2273 
2274  std::vector<uint8_t> client_data = clientid.getClientId();
2275  unsigned long client_data_length = client_data.size();
2276 
2277  // If the data happens to be empty, we have to create a 1 byte dummy
2278  // buffer and pass it to the binding.
2279  if (client_data.empty()) {
2280  client_data.resize(1);
2281  }
2282 
2283  inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
2284  inbind[0].buffer_length = client_data_length;
2285  inbind[0].length = &client_data_length;
2286 
2287  // Get the data
2288  Lease4Collection result;
2289 
2290  // Get a context
2291  MySqlLeaseContextAlloc get_context(*this);
2292  MySqlLeaseContextPtr ctx = get_context.ctx_;
2293 
2294  getLeaseCollection(ctx, GET_LEASE4_CLIENTID, inbind, result);
2295 
2296  return (result);
2297 }
2298 
2299 Lease4Ptr
2300 MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
2302  .arg(subnet_id)
2303  .arg(clientid.toText());
2304 
2305  // Set up the WHERE clause value
2306  MYSQL_BIND inbind[2];
2307  memset(inbind, 0, sizeof(inbind));
2308 
2309  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2310 
2311  std::vector<uint8_t> client_data = clientid.getClientId();
2312  unsigned long client_data_length = client_data.size();
2313 
2314  // If the data happens to be empty, we have to create a 1 byte dummy
2315  // buffer and pass it to the binding.
2316  if (client_data.empty()) {
2317  client_data.resize(1);
2318  }
2319 
2320  inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
2321  inbind[0].buffer_length = client_data_length;
2322  inbind[0].length = &client_data_length;
2323 
2324  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2325  inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
2326  inbind[1].is_unsigned = MLM_TRUE;
2327 
2328  // Get the data
2329  Lease4Ptr result;
2330 
2331  // Get a context
2332  MySqlLeaseContextAlloc get_context(*this);
2333  MySqlLeaseContextPtr ctx = get_context.ctx_;
2334 
2335  getLease(ctx, GET_LEASE4_CLIENTID_SUBID, inbind, result);
2336 
2337  return (result);
2338 }
2339 
2343  .arg(subnet_id);
2344 
2345  // Set up the WHERE clause value
2346  MYSQL_BIND inbind[1];
2347  memset(inbind, 0, sizeof(inbind));
2348 
2349  // Subnet ID
2350  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2351  inbind[0].buffer = reinterpret_cast<char*>(&subnet_id);
2352  inbind[0].is_unsigned = MLM_TRUE;
2353 
2354  // ... and get the data
2355  Lease4Collection result;
2356 
2357  // Get a context
2358  MySqlLeaseContextAlloc get_context(*this);
2359  MySqlLeaseContextPtr ctx = get_context.ctx_;
2360 
2361  getLeaseCollection(ctx, GET_LEASE4_SUBID, inbind, result);
2362 
2363  return (result);
2364 }
2365 
2367 MySqlLeaseMgr::getLeases4(const std::string& hostname) const {
2369  .arg(hostname);
2370 
2371  // Set up the WHERE clause value
2372  MYSQL_BIND inbind[1];
2373  memset(inbind, 0, sizeof(inbind));
2374 
2375  // Hostname
2376  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2377  inbind[0].buffer = const_cast<char*>(hostname.c_str());
2378  inbind[0].buffer_length = hostname.length();
2379 
2380  // ... and get the data
2381  Lease4Collection result;
2382 
2383  // Get a context
2384  MySqlLeaseContextAlloc get_context(*this);
2385  MySqlLeaseContextPtr ctx = get_context.ctx_;
2386 
2387  getLeaseCollection(ctx, GET_LEASE4_HOSTNAME, inbind, result);
2388 
2389  return (result);
2390 }
2391 
2395 
2396  Lease4Collection result;
2397 
2398  // Get a context
2399  MySqlLeaseContextAlloc get_context(*this);
2400  MySqlLeaseContextPtr ctx = get_context.ctx_;
2401 
2402  getLeaseCollection(ctx, GET_LEASE4, 0, result);
2403 
2404  return (result);
2405 }
2406 
2408 MySqlLeaseMgr::getLeases4(const IOAddress& lower_bound_address,
2409  const LeasePageSize& page_size) const {
2410  // Expecting IPv4 address.
2411  if (!lower_bound_address.isV4()) {
2412  isc_throw(InvalidAddressFamily, "expected IPv4 address while "
2413  "retrieving leases from the lease database, got "
2414  << lower_bound_address);
2415  }
2416 
2418  .arg(page_size.page_size_)
2419  .arg(lower_bound_address.toText());
2420 
2421  // Prepare WHERE clause
2422  MYSQL_BIND inbind[2];
2423  memset(inbind, 0, sizeof(inbind));
2424 
2425  // Bind lower bound address
2426  uint32_t lb_address_data = lower_bound_address.toUint32();
2427  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2428  inbind[0].buffer = reinterpret_cast<char*>(&lb_address_data);
2429  inbind[0].is_unsigned = MLM_TRUE;
2430 
2431  // Bind page size value
2432  size_t* ps = const_cast<size_t*>(&page_size.page_size_);
2433  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2434  inbind[1].buffer = reinterpret_cast<char*>(ps);
2435  inbind[1].is_unsigned = MLM_TRUE;
2436 
2437  // Get the leases
2438  Lease4Collection result;
2439 
2440  // Get a context
2441  MySqlLeaseContextAlloc get_context(*this);
2442  MySqlLeaseContextPtr ctx = get_context.ctx_;
2443 
2444  getLeaseCollection(ctx, GET_LEASE4_PAGE, inbind, result);
2445 
2446  return (result);
2447 }
2448 
2449 Lease6Ptr
2451  const IOAddress& addr) const {
2453  .arg(addr.toText())
2454  .arg(lease_type);
2455 
2456  // Set up the WHERE clause value
2457  MYSQL_BIND inbind[2];
2458  memset(inbind, 0, sizeof(inbind));
2459 
2460  std::string addr6 = addr.toText();
2461  unsigned long addr6_length = addr6.size();
2462 
2463  // See the earlier description of the use of "const_cast" when accessing
2464  // the address for an explanation of the reason.
2465  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2466  inbind[0].buffer = const_cast<char*>(addr6.c_str());
2467  inbind[0].buffer_length = addr6_length;
2468  inbind[0].length = &addr6_length;
2469 
2470  // LEASE_TYPE
2471  inbind[1].buffer_type = MYSQL_TYPE_TINY;
2472  inbind[1].buffer = reinterpret_cast<char*>(&lease_type);
2473  inbind[1].is_unsigned = MLM_TRUE;
2474 
2475  Lease6Ptr result;
2476 
2477  // Get a context
2478  MySqlLeaseContextAlloc get_context(*this);
2479  MySqlLeaseContextPtr ctx = get_context.ctx_;
2480 
2481  getLease(ctx, GET_LEASE6_ADDR, inbind, result);
2482 
2483  return (result);
2484 }
2485 
2488  uint32_t iaid) const {
2490  .arg(iaid)
2491  .arg(duid.toText())
2492  .arg(lease_type);
2493 
2494  // Set up the WHERE clause value
2495  MYSQL_BIND inbind[3];
2496  memset(inbind, 0, sizeof(inbind));
2497 
2498  // In the following statement, the DUID is being read. However, the
2499  // MySQL C interface does not use "const", so the "buffer" element
2500  // is declared as "char*" instead of "const char*". To resolve this,
2501  // the "const" is discarded before the uint8_t* is cast to char*.
2502  //
2503  // Note that the const_cast could be avoided by copying the DUID to
2504  // a writable buffer and storing the address of that in the "buffer"
2505  // element. However, this introduces a copy operation (with additional
2506  // overhead) purely to get round the structures introduced by design of
2507  // the MySQL interface (which uses the area pointed to by "buffer" as
2508  // input when specifying query parameters and as output when retrieving
2509  // data). For that reason, "const_cast" has been used.
2510  const vector<uint8_t>& duid_vector = duid.getDuid();
2511  unsigned long duid_length = duid_vector.size();
2512 
2513  // Make sure that the buffer has at least length of 1, even if
2514  // empty client id is passed. This is required by some of the
2515  // MySQL connectors that the buffer is set to non-null value.
2516  // Otherwise, null value would be inserted into the database,
2517  // rather than empty string.
2518  uint8_t single_byte_data = 0;
2519  uint8_t* data = !duid_vector.empty() ? const_cast<uint8_t*>(&duid_vector[0])
2520  : &single_byte_data;
2521 
2522  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2523  inbind[0].buffer = reinterpret_cast<char*>(data);
2524  inbind[0].buffer_length = duid_length;
2525  inbind[0].length = &duid_length;
2526 
2527  // IAID
2528  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2529  inbind[1].buffer = reinterpret_cast<char*>(&iaid);
2530  inbind[1].is_unsigned = MLM_TRUE;
2531 
2532  // LEASE_TYPE
2533  inbind[2].buffer_type = MYSQL_TYPE_TINY;
2534  inbind[2].buffer = reinterpret_cast<char*>(&lease_type);
2535  inbind[2].is_unsigned = MLM_TRUE;
2536 
2537  // ... and get the data
2538  Lease6Collection result;
2539 
2540  // Get a context
2541  MySqlLeaseContextAlloc get_context(*this);
2542  MySqlLeaseContextPtr ctx = get_context.ctx_;
2543 
2544  getLeaseCollection(ctx, GET_LEASE6_DUID_IAID, inbind, result);
2545 
2546  return (result);
2547 }
2548 
2551  uint32_t iaid, SubnetID subnet_id) const {
2553  .arg(iaid)
2554  .arg(subnet_id)
2555  .arg(duid.toText())
2556  .arg(lease_type);
2557 
2558  // Set up the WHERE clause value
2559  MYSQL_BIND inbind[4];
2560  memset(inbind, 0, sizeof(inbind));
2561 
2562  // See the earlier description of the use of "const_cast" when accessing
2563  // the DUID for an explanation of the reason.
2564  const vector<uint8_t>& duid_vector = duid.getDuid();
2565  unsigned long duid_length = duid_vector.size();
2566  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2567  inbind[0].buffer = reinterpret_cast<char*>(
2568  const_cast<uint8_t*>(&duid_vector[0]));
2569  inbind[0].buffer_length = duid_length;
2570  inbind[0].length = &duid_length;
2571 
2572  // IAID
2573  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2574  inbind[1].buffer = reinterpret_cast<char*>(&iaid);
2575  inbind[1].is_unsigned = MLM_TRUE;
2576 
2577  // Subnet ID
2578  inbind[2].buffer_type = MYSQL_TYPE_LONG;
2579  inbind[2].buffer = reinterpret_cast<char*>(&subnet_id);
2580  inbind[2].is_unsigned = MLM_TRUE;
2581 
2582  // LEASE_TYPE
2583  inbind[3].buffer_type = MYSQL_TYPE_TINY;
2584  inbind[3].buffer = reinterpret_cast<char*>(&lease_type);
2585  inbind[3].is_unsigned = MLM_TRUE;
2586 
2587  // ... and get the data
2588  Lease6Collection result;
2589 
2590  // Get a context
2591  MySqlLeaseContextAlloc get_context(*this);
2592  MySqlLeaseContextPtr ctx = get_context.ctx_;
2593 
2594  getLeaseCollection(ctx, GET_LEASE6_DUID_IAID_SUBID, inbind, result);
2595 
2596  return (result);
2597 }
2598 
2602  .arg(subnet_id);
2603 
2604  // Set up the WHERE clause value
2605  MYSQL_BIND inbind[1];
2606  memset(inbind, 0, sizeof(inbind));
2607 
2608  // Subnet ID
2609  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2610  inbind[0].buffer = reinterpret_cast<char*>(&subnet_id);
2611  inbind[0].is_unsigned = MLM_TRUE;
2612 
2613  // ... and get the data
2614  Lease6Collection result;
2615 
2616  // Get a context
2617  MySqlLeaseContextAlloc get_context(*this);
2618  MySqlLeaseContextPtr ctx = get_context.ctx_;
2619 
2620  getLeaseCollection(ctx, GET_LEASE6_SUBID, inbind, result);
2621 
2622  return (result);
2623 }
2624 
2628 
2629  Lease6Collection result;
2630 
2631  // Get a context
2632  MySqlLeaseContextAlloc get_context(*this);
2633  MySqlLeaseContextPtr ctx = get_context.ctx_;
2634 
2635  getLeaseCollection(ctx, GET_LEASE6, 0, result);
2636 
2637  return (result);
2638 }
2639 
2641 MySqlLeaseMgr::getLeases6(const DUID& duid) const {
2643  .arg(duid.toText());
2644 
2645  // Set up the WHERE clause value
2646  MYSQL_BIND inbind[1];
2647  memset(inbind, 0, sizeof(inbind));
2648 
2649  const vector<uint8_t>& duid_vector = duid.getDuid();
2650  unsigned long duid_length = duid_vector.size();
2651 
2652  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2653  inbind[0].buffer = reinterpret_cast<char*>(
2654  const_cast<uint8_t*>(&duid_vector[0]));
2655  inbind[0].buffer_length = duid_length;
2656  inbind[0].length = &duid_length;
2657 
2658  Lease6Collection result;
2659 
2660  // Get a context
2661  MySqlLeaseContextAlloc get_context(*this);
2662  MySqlLeaseContextPtr ctx = get_context.ctx_;
2663 
2664  getLeaseCollection(ctx, GET_LEASE6_DUID, inbind, result);
2665 
2666  return result;
2667 }
2668 
2670 MySqlLeaseMgr::getLeases6(const std::string& hostname) const {
2672  .arg(hostname);
2673 
2674  // Set up the WHERE clause value
2675  MYSQL_BIND inbind[1];
2676  memset(inbind, 0, sizeof(inbind));
2677 
2678  // Hostname
2679  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2680  inbind[0].buffer = const_cast<char*>(hostname.c_str());
2681  inbind[0].buffer_length = hostname.length();
2682 
2683  // ... and get the data
2684  Lease6Collection result;
2685 
2686  // Get a context
2687  MySqlLeaseContextAlloc get_context(*this);
2688  MySqlLeaseContextPtr ctx = get_context.ctx_;
2689 
2690  getLeaseCollection(ctx, GET_LEASE6_HOSTNAME, inbind, result);
2691 
2692  return (result);
2693 }
2694 
2696 MySqlLeaseMgr::getLeases6(const IOAddress& lower_bound_address,
2697  const LeasePageSize& page_size) const {
2698  // Expecting IPv6 address.
2699  if (!lower_bound_address.isV6()) {
2700  isc_throw(InvalidAddressFamily, "expected IPv6 address while "
2701  "retrieving leases from the lease database, got "
2702  << lower_bound_address);
2703  }
2704 
2706  .arg(page_size.page_size_)
2707  .arg(lower_bound_address.toText());
2708 
2709  // Prepare WHERE clause
2710  MYSQL_BIND inbind[2];
2711  memset(inbind, 0, sizeof(inbind));
2712 
2713  // In IPv6 we compare addresses represented as strings. The IPv6 zero address
2714  // is ::, so it is greater than any other address. In this special case, we
2715  // just use 0 for comparison which should be lower than any real IPv6 address.
2716  std::string lb_address_data = "0";
2717  if (!lower_bound_address.isV6Zero()) {
2718  lb_address_data = lower_bound_address.toText();
2719  }
2720 
2721  // Bind lower bound address
2722  unsigned long lb_address_data_size = lb_address_data.size();
2723  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2724  inbind[0].buffer = const_cast<char*>(lb_address_data.c_str());
2725  inbind[0].buffer_length = lb_address_data_size;
2726  inbind[0].length = &lb_address_data_size;
2727 
2728  // Bind page size value
2729  size_t* ps = const_cast<size_t*>(&page_size.page_size_);
2730  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2731  inbind[1].buffer = reinterpret_cast<char*>(ps);
2732  inbind[1].is_unsigned = MLM_TRUE;
2733 
2734  // Get the leases
2735  Lease6Collection result;
2736 
2737  // Get a context
2738  MySqlLeaseContextAlloc get_context(*this);
2739  MySqlLeaseContextPtr ctx = get_context.ctx_;
2740 
2741  getLeaseCollection(ctx, GET_LEASE6_PAGE, inbind, result);
2742 
2743  return (result);
2744 }
2745 
2746 void
2748  const size_t max_leases) const {
2750  .arg(max_leases);
2751  getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
2752 }
2753 
2754 void
2756  const size_t max_leases) const {
2758  .arg(max_leases);
2759  getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
2760 }
2761 
2762 template<typename LeaseCollection>
2763 void
2764 MySqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
2765  const size_t max_leases,
2766  StatementIndex statement_index) const {
2767  // Set up the WHERE clause value
2768  MYSQL_BIND inbind[3];
2769  memset(inbind, 0, sizeof(inbind));
2770 
2771  // Exclude reclaimed leases.
2772  uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
2773  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2774  inbind[0].buffer = reinterpret_cast<char*>(&state);
2775  inbind[0].is_unsigned = MLM_TRUE;
2776 
2777  // Expiration timestamp.
2778  MYSQL_TIME expire_time;
2779  MySqlConnection::convertToDatabaseTime(time(0), expire_time);
2780  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2781  inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
2782  inbind[1].buffer_length = sizeof(expire_time);
2783 
2784  // If the number of leases is 0, we will return all leases. This is
2785  // achieved by setting the limit to a very high value.
2786  uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
2787  std::numeric_limits<uint32_t>::max();
2788  inbind[2].buffer_type = MYSQL_TYPE_LONG;
2789  inbind[2].buffer = reinterpret_cast<char*>(&limit);
2790  inbind[2].is_unsigned = MLM_TRUE;
2791 
2792  // Get a context
2793  MySqlLeaseContextAlloc get_context(*this);
2794  MySqlLeaseContextPtr ctx = get_context.ctx_;
2795 
2796  // Get the data
2797  getLeaseCollection(ctx, statement_index, inbind, expired_leases);
2798 }
2799 
2800 // Update lease methods. These comprise common code that handles the actual
2801 // update, and type-specific methods that set up the parameters for the prepared
2802 // statement depending on the type of lease.
2803 
2804 template <typename LeasePtr>
2805 void
2806 MySqlLeaseMgr::updateLeaseCommon(MySqlLeaseContextPtr& ctx,
2807  StatementIndex stindex,
2808  MYSQL_BIND* bind,
2809  const LeasePtr& lease) {
2810 
2811  // Bind the parameters to the statement
2812  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], bind);
2813  checkError(ctx, status, stindex, "unable to bind parameters");
2814 
2815  // Execute
2816  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2817  checkError(ctx, status, stindex, "unable to execute");
2818 
2819  // See how many rows were affected. The statement should only update a
2820  // single row.
2821  int affected_rows = mysql_stmt_affected_rows(ctx->conn_.statements_[stindex]);
2822 
2823  // Check success case first as it is the most likely outcome.
2824  if (affected_rows == 1) {
2825  return;
2826  }
2827 
2828  // If no rows affected, lease doesn't exist.
2829  if (affected_rows == 0) {
2830  isc_throw(NoSuchLease, "unable to update lease for address " <<
2831  lease->addr_.toText() << " as it does not exist");
2832  }
2833 
2834  // Should not happen - primary key constraint should only have selected
2835  // one row.
2836  isc_throw(DbOperationError, "apparently updated more than one lease "
2837  "that had the address " << lease->addr_.toText());
2838 }
2839 
2840 void
2842  const StatementIndex stindex = UPDATE_LEASE4;
2843 
2845  .arg(lease->addr_.toText());
2846 
2847  // Get a context
2848  MySqlLeaseContextAlloc get_context(*this);
2849  MySqlLeaseContextPtr ctx = get_context.ctx_;
2850 
2851  // Create the MYSQL_BIND array for the data being updated
2852  std::vector<MYSQL_BIND> bind = ctx->exchange4_->createBindForSend(lease);
2853 
2854  // Set up the WHERE clause and append it to the MYSQL_BIND array
2855  MYSQL_BIND inbind[2];
2856  memset(inbind, 0, sizeof(inbind));
2857 
2858  uint32_t addr4 = lease->addr_.toUint32();
2859  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2860  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
2861  inbind[0].is_unsigned = MLM_TRUE;
2862 
2863  bind.push_back(inbind[0]);
2864 
2865  // See the expire code of createBindForSend for the
2866  // infinite valid lifetime special case.
2867  MYSQL_TIME expire;
2868  uint32_t valid_lft = lease->current_valid_lft_;
2869  if (valid_lft == Lease::INFINITY_LFT) {
2870  valid_lft = 0;
2871  }
2872  MySqlConnection::convertToDatabaseTime(lease->current_cltt_, valid_lft,
2873  expire);
2874  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2875  inbind[1].buffer = reinterpret_cast<char*>(&expire);
2876  inbind[1].buffer_length = sizeof(expire);
2877 
2878  bind.push_back(inbind[1]);
2879 
2880  // Drop to common update code
2881  updateLeaseCommon(ctx, stindex, &bind[0], lease);
2882 
2883  // Update lease current expiration time.
2884  lease->updateCurrentExpirationTime();
2885 }
2886 
2887 void
2889  const StatementIndex stindex = UPDATE_LEASE6;
2890 
2892  .arg(lease->addr_.toText())
2893  .arg(lease->type_);
2894 
2895  // Get a context
2896  MySqlLeaseContextAlloc get_context(*this);
2897  MySqlLeaseContextPtr ctx = get_context.ctx_;
2898 
2899  // Create the MYSQL_BIND array for the data being updated
2900  std::vector<MYSQL_BIND> bind = ctx->exchange6_->createBindForSend(lease);
2901 
2902  // Set up the WHERE clause and append it to the MYSQL_BIND array
2903  MYSQL_BIND inbind[2];
2904  memset(inbind, 0, sizeof(inbind));
2905 
2906  std::string addr6 = lease->addr_.toText();
2907  unsigned long addr6_length = addr6.size();
2908 
2909  // See the earlier description of the use of "const_cast" when accessing
2910  // the address for an explanation of the reason.
2911  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2912  inbind[0].buffer = const_cast<char*>(addr6.c_str());
2913  inbind[0].buffer_length = addr6_length;
2914  inbind[0].length = &addr6_length;
2915 
2916  bind.push_back(inbind[0]);
2917 
2918  // See the expire code of createBindForSend for the
2919  // infinite valid lifetime special case.
2920  MYSQL_TIME expire;
2921  uint32_t valid_lft = lease->current_valid_lft_;
2922  if (valid_lft == Lease::INFINITY_LFT) {
2923  valid_lft = 0;
2924  }
2925  MySqlConnection::convertToDatabaseTime(lease->current_cltt_, valid_lft,
2926  expire);
2927  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2928  inbind[1].buffer = reinterpret_cast<char*>(&expire);
2929  inbind[1].buffer_length = sizeof(expire);
2930 
2931  bind.push_back(inbind[1]);
2932 
2933  // Drop to common update code
2934  updateLeaseCommon(ctx, stindex, &bind[0], lease);
2935 
2936  // Update lease current expiration time.
2937  lease->updateCurrentExpirationTime();
2938 }
2939 
2940 // Delete lease methods. Similar to other groups of methods, these comprise
2941 // a per-type method that sets up the relevant MYSQL_BIND array (in this
2942 // case, a single method for both V4 and V6 addresses) and a common method that
2943 // handles the common processing.
2944 
2945 uint64_t
2946 MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
2947  MYSQL_BIND* bind) {
2948 
2949  // Get a context
2950  MySqlLeaseContextAlloc get_context(*this);
2951  MySqlLeaseContextPtr ctx = get_context.ctx_;
2952 
2953  // Bind the input parameters to the statement
2954  int status = mysql_stmt_bind_param(ctx->conn_.statements_[stindex], bind);
2955  checkError(ctx, status, stindex, "unable to bind WHERE clause parameter");
2956 
2957  // Execute
2958  status = MysqlExecuteStatement(ctx->conn_.statements_[stindex]);
2959  checkError(ctx, status, stindex, "unable to execute");
2960 
2961  // See how many rows were affected. Note that the statement may delete
2962  // multiple rows.
2963  return (static_cast<uint64_t>(mysql_stmt_affected_rows(ctx->conn_.statements_[stindex])));
2964 }
2965 
2966 bool
2968  const IOAddress& addr = lease->addr_;
2970  .arg(addr.toText());
2971 
2972  // Set up the WHERE clause value
2973  MYSQL_BIND inbind[2];
2974  memset(inbind, 0, sizeof(inbind));
2975 
2976  uint32_t addr4 = addr.toUint32();
2977 
2978  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2979  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
2980  inbind[0].is_unsigned = MLM_TRUE;
2981 
2982  // See the expire code of createBindForSend for the
2983  // infinite valid lifetime special case.
2984  MYSQL_TIME expire;
2985  uint32_t valid_lft = lease->current_valid_lft_;
2986  if (valid_lft == Lease::INFINITY_LFT) {
2987  valid_lft = 0;
2988  }
2989  MySqlConnection::convertToDatabaseTime(lease->current_cltt_, valid_lft,
2990  expire);
2991  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2992  inbind[1].buffer = reinterpret_cast<char*>(&expire);
2993  inbind[1].buffer_length = sizeof(expire);
2994 
2995  auto affected_rows = deleteLeaseCommon(DELETE_LEASE4, inbind);
2996 
2997  // Check success case first as it is the most likely outcome.
2998  if (affected_rows == 1) {
2999  return (true);
3000  }
3001 
3002  // If no rows affected, lease doesn't exist.
3003  if (affected_rows == 0) {
3004  return (false);
3005  }
3006 
3007  // Should not happen - primary key constraint should only have selected
3008  // one row.
3009  isc_throw(DbOperationError, "apparently deleted more than one lease "
3010  "that had the address " << lease->addr_.toText());
3011 }
3012 
3013 bool
3015  const IOAddress& addr = lease->addr_;
3018  .arg(addr.toText());
3019 
3020  // Set up the WHERE clause value
3021  MYSQL_BIND inbind[2];
3022  memset(inbind, 0, sizeof(inbind));
3023 
3024  std::string addr6 = addr.toText();
3025  unsigned long addr6_length = addr6.size();
3026 
3027  // See the earlier description of the use of "const_cast" when accessing
3028  // the address for an explanation of the reason.
3029  inbind[0].buffer_type = MYSQL_TYPE_STRING;
3030  inbind[0].buffer = const_cast<char*>(addr6.c_str());
3031  inbind[0].buffer_length = addr6_length;
3032  inbind[0].length = &addr6_length;
3033 
3034  // See the expire code of createBindForSend for the
3035  // infinite valid lifetime special case.
3036  MYSQL_TIME expire;
3037  uint32_t valid_lft = lease->current_valid_lft_;
3038  if (valid_lft == Lease::INFINITY_LFT) {
3039  valid_lft = 0;
3040  }
3041  MySqlConnection::convertToDatabaseTime(lease->current_cltt_, valid_lft,
3042  expire);
3043  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
3044  inbind[1].buffer = reinterpret_cast<char*>(&expire);
3045  inbind[1].buffer_length = sizeof(expire);
3046 
3047  auto affected_rows = deleteLeaseCommon(DELETE_LEASE6, inbind);
3048 
3049  // Check success case first as it is the most likely outcome.
3050  if (affected_rows == 1) {
3051  return (true);
3052  }
3053 
3054  // If no rows affected, lease doesn't exist.
3055  if (affected_rows == 0) {
3056  return (false);
3057  }
3058 
3059  // Should not happen - primary key constraint should only have selected
3060  // one row.
3061  isc_throw(DbOperationError, "apparently deleted more than one lease "
3062  "that had the address " << lease->addr_.toText());
3063 }
3064 
3065 uint64_t
3068  .arg(secs);
3069  return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
3070 }
3071 
3072 uint64_t
3075  .arg(secs);
3076  return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
3077 }
3078 
3079 uint64_t
3080 MySqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
3081  StatementIndex statement_index) {
3082  // Set up the WHERE clause value
3083  MYSQL_BIND inbind[2];
3084  memset(inbind, 0, sizeof(inbind));
3085 
3086  // State is reclaimed.
3087  uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
3088  inbind[0].buffer_type = MYSQL_TYPE_LONG;
3089  inbind[0].buffer = reinterpret_cast<char*>(&state);
3090  inbind[0].is_unsigned = MLM_TRUE;
3091 
3092  // Expiration timestamp.
3093  MYSQL_TIME expire_time;
3094  MySqlConnection::convertToDatabaseTime(time(0) - static_cast<time_t>(secs), expire_time);
3095  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
3096  inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
3097  inbind[1].buffer_length = sizeof(expire_time);
3098 
3099  // Get the number of deleted leases and log it.
3100  uint64_t deleted_leases = deleteLeaseCommon(statement_index, inbind);
3102  .arg(deleted_leases);
3103 
3104  return (deleted_leases);
3105 }
3106 
3107 string
3108 MySqlLeaseMgr::checkLimits(ConstElementPtr const& user_context, StatementIndex const stindex) const {
3109  // No user context means no limits means allocation allowed means empty string.
3110  if (!user_context) {
3111  return string();
3112  }
3113 
3114  // Get a context.
3115  MySqlLeaseContextAlloc get_context(*this);
3116  MySqlLeaseContextPtr ctx = get_context.ctx_;
3117 
3118  // Create bindings.
3119  MySqlBindingCollection in_bindings({
3120  MySqlBinding::createString(user_context->str())
3121  });
3122  MySqlBindingCollection out_bindings({
3123  MySqlBinding::createString(LIMITS_TEXT_MAX_LEN)
3124  });
3125 
3126  // Execute the select.
3127  std::string limit_text;
3128  ctx->conn_.selectQuery(stindex, in_bindings, out_bindings,
3129  [&limit_text] (MySqlBindingCollection const& result) {
3130  limit_text = result[0]->getString();
3131  });
3132 
3133  return limit_text;
3134 }
3135 
3136 string
3137 MySqlLeaseMgr::checkLimits4(ConstElementPtr const& user_context) const {
3138  return checkLimits(user_context, CHECK_LEASE4_LIMITS);
3139 }
3140 
3141 string
3142 MySqlLeaseMgr::checkLimits6(ConstElementPtr const& user_context) const {
3143  return checkLimits(user_context, CHECK_LEASE6_LIMITS);
3144 }
3145 
3146 bool
3147 MySqlLeaseMgr::isJsonSupported() const {
3148  // Get a context.
3149  MySqlLeaseContextAlloc get_context(*this);
3150  MySqlLeaseContextPtr ctx = get_context.ctx_;
3151 
3152  // Create bindings.
3153  MySqlBindingCollection in_bindings;
3154  MySqlBindingCollection out_bindings({
3156  });
3157 
3158  // Execute the select.
3159  bool json_supported(false);
3160  ctx->conn_.selectQuery(IS_JSON_SUPPORTED, in_bindings, out_bindings,
3161  [&json_supported] (MySqlBindingCollection const& result) {
3162  json_supported = result[0]->getBool();
3163  });
3164 
3165  return json_supported;
3166 }
3167 
3168 size_t
3169 MySqlLeaseMgr::getClassLeaseCount(const ClientClass& client_class,
3170  const Lease::Type& ltype /* = Lease::TYPE_V4*/) const {
3171  // Get a context.
3172  MySqlLeaseContextAlloc get_context(*this);
3173  MySqlLeaseContextPtr ctx = get_context.ctx_;
3174 
3175  // Create bindings.
3176  MySqlBindingCollection in_bindings({
3177  MySqlBinding::createString(client_class)
3178  });
3179  if (ltype != Lease::TYPE_V4) {
3180  in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(ltype));
3181  }
3182  MySqlBindingCollection out_bindings({
3183  MySqlBinding::createInteger<int64_t>()
3184  });
3185 
3186  // Execute the select.
3187  StatementIndex const stindex(ltype == Lease::TYPE_V4 ? GET_LEASE4_COUNT_BY_CLASS :
3189  size_t count(0);
3190  ctx->conn_.selectQuery(stindex, in_bindings, out_bindings,
3191  [&count] (MySqlBindingCollection const& result) {
3192  count = result[0]->getInteger<int64_t>();
3193  });
3194 
3195  return count;
3196 }
3197 
3198 void
3199 MySqlLeaseMgr::recountClassLeases4() {
3200  isc_throw(NotImplemented, "MySqlLeaseMgr::recountClassLeases4() not implemented");
3201 }
3202 
3203 void
3204 MySqlLeaseMgr::recountClassLeases6() {
3205  isc_throw(NotImplemented, "MySqlLeaseMgr::recountClassLeases6() not implemented");
3206 }
3207 
3208 void
3209 MySqlLeaseMgr::clearClassLeaseCounts() {
3210  isc_throw(NotImplemented, "MySqlLeaseMgr::clearClassLeaseCounts() not implemented");
3211 }
3212 
3213 void
3214 MySqlLeaseMgr::writeLeases4(const std::string&) {
3215  isc_throw(NotImplemented, "MySqlLeaseMgr::writeLeases4() not implemented");
3216 }
3217 
3218 void
3219 MySqlLeaseMgr::writeLeases6(const std::string&) {
3220  isc_throw(NotImplemented, "MySqlLeaseMgr::writeLeases6() not implemented");
3221 }
3222 
3225  // Get a context
3226  MySqlLeaseContextAlloc get_context(*this);
3227  MySqlLeaseContextPtr ctx = get_context.ctx_;
3228 
3229  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3231  false));
3232  query->start();
3233  return(query);
3234 }
3235 
3238  // Get a context
3239  MySqlLeaseContextAlloc get_context(*this);
3240  MySqlLeaseContextPtr ctx = get_context.ctx_;
3241 
3242  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3244  false,
3245  subnet_id));
3246  query->start();
3247  return(query);
3248 }
3249 
3252  const SubnetID& last_subnet_id) {
3253  // Get a context
3254  MySqlLeaseContextAlloc get_context(*this);
3255  MySqlLeaseContextPtr ctx = get_context.ctx_;
3256 
3257  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3259  false,
3260  first_subnet_id,
3261  last_subnet_id));
3262  query->start();
3263  return(query);
3264 }
3265 
3268  // Get a context
3269  MySqlLeaseContextAlloc get_context(*this);
3270  MySqlLeaseContextPtr ctx = get_context.ctx_;
3271 
3272  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3274  true));
3275  query->start();
3276  return(query);
3277 }
3278 
3281  // Get a context
3282  MySqlLeaseContextAlloc get_context(*this);
3283  MySqlLeaseContextPtr ctx = get_context.ctx_;
3284 
3285  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3287  true,
3288  subnet_id));
3289  query->start();
3290  return(query);
3291 }
3292 
3295  const SubnetID& last_subnet_id) {
3296  // Get a context
3297  MySqlLeaseContextAlloc get_context(*this);
3298  MySqlLeaseContextPtr ctx = get_context.ctx_;
3299 
3300  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(ctx->conn_,
3302  true,
3303  first_subnet_id,
3304  last_subnet_id));
3305  query->start();
3306  return(query);
3307 }
3308 
3309 size_t
3310 MySqlLeaseMgr::wipeLeases4(const SubnetID& /*subnet_id*/) {
3311  isc_throw(NotImplemented, "wipeLeases4 is not implemented for MySQL backend");
3312 }
3313 
3314 size_t
3315 MySqlLeaseMgr::wipeLeases6(const SubnetID& /*subnet_id*/) {
3316  isc_throw(NotImplemented, "wipeLeases6 is not implemented for MySQL backend");
3317 }
3318 
3319 // Miscellaneous database methods.
3320 
3321 std::string
3323  // Get a context
3324  MySqlLeaseContextAlloc get_context(*this);
3325  MySqlLeaseContextPtr ctx = get_context.ctx_;
3326 
3327  std::string name = "";
3328  try {
3329  name = ctx->conn_.getParameter("name");
3330  } catch (...) {
3331  // Return an empty name
3332  }
3333  return (name);
3334 }
3335 
3336 std::string
3338  return (std::string("MySQL Database"));
3339 }
3340 
3341 std::pair<uint32_t, uint32_t>
3344 
3345  return (MySqlConnection::getVersion(parameters_));
3346 }
3347 
3348 void
3351 }
3352 
3353 void
3356 }
3357 
3358 void
3359 MySqlLeaseMgr::checkError(MySqlLeaseContextPtr& ctx,
3360  int status, StatementIndex index,
3361  const char* what) const {
3362  ctx->conn_.checkError(status, index, what);
3363 }
3364 
3366 MySqlLeaseMgr::getLeases4ByRelayId(const OptionBuffer& /* relay_id */,
3367  const IOAddress& /* lower_bound_address */,
3368  const LeasePageSize& /* page_size */,
3369  const time_t& /* qry_start_time = 0 */,
3370  const time_t& /* qry_end_time = 0 */) {
3371  isc_throw(NotImplemented, "MySqlLeaseMgr::getLeases4ByRelayId not implemented");
3372 }
3373 
3375 MySqlLeaseMgr::getLeases4ByRemoteId(const OptionBuffer& /* remote_id */,
3376  const IOAddress& /* lower_bound_address */,
3377  const LeasePageSize& /* page_size */,
3378  const time_t& /* qry_start_time = 0 */,
3379  const time_t& /* qry_end_time = 0 */) {
3380  isc_throw(NotImplemented, "MySqlLeaseMgr::getLeases4ByRemoteId not implemented");
3381 }
3382 
3384 MySqlLeaseMgr::getLeases6ByRelayId(const DUID& /* relay_id */,
3385  const IOAddress& /* link_addr */,
3386  const IOAddress& /* lower_bound_address */,
3387  const LeasePageSize& /* page_size */) {
3388  isc_throw(NotImplemented, "MySqlLeaseMgr::getLeases6ByRelayId not implemented");
3389 }
3390 
3392 MySqlLeaseMgr::getLeases6ByRemoteId(const OptionBuffer& /* remote_id */,
3393  const IOAddress& /* link_addr */,
3394  const IOAddress& /* lower_bound_address */,
3395  const LeasePageSize& /* page_size*/) {
3396  isc_throw(NotImplemented, "MySqlLeaseMgr::getLeases6ByRemoteId not implemented");
3397 }
3398 
3400 MySqlLeaseMgr::getLeases6ByLink(const IOAddress& /* link_addr */,
3401  const IOAddress& /* lower_bound_address */,
3402  const LeasePageSize& /* page_size */) {
3403  isc_throw(NotImplemented, "MySqlLeaseMgr::getLeases6ByLink not implemented");
3404 }
3405 
3406 } // namespace dhcp
3407 } // 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.
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:75
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
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs) override
Deletes all expired-reclaimed DHCPv6 leases.
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.
virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery4(const SubnetID &first_subnet_id, const SubnetID &last_subnet_id) override
Creates and runs the IPv4 lease stats query for a single subnet.
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.
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:498
const uint32_t MYSQL_SCHEMA_VERSION_MAJOR
const isc::log::MessageID DHCPSRV_MYSQL_DELETED_EXPIRED_RECLAIMED
virtual LeaseStatsQueryPtr startLeaseStatsQuery6() override
Creates and runs the IPv6 lease stats query.
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
const isc::log::MessageID DHCPSRV_MYSQL_GET_DUID
boost::shared_ptr< Lease > LeasePtr
Pointer to the lease object.
Definition: lease.h:22
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.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery6(const SubnetID &subnet_id) override
Creates and runs the IPv6 lease stats query for a single subnet.
Holds DUID (DHCPv6 Unique Identifier)
Definition: duid.h:27
static MySqlBindingPtr createString(const unsigned long length)
Creates binding of text type for receiving data.
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.
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...
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:284
bool getNextRow(LeaseStatsRow &row)
Fetches the next row in the result set.
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const override
Returns an IPv4 lease for specified IPv4 address.
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.
IPv4 lease.
Definition: lease.h:50
virtual ~MySqlLeaseMgr()
Destructor (closes database)
virtual void updateLease4(const Lease4Ptr &lease4) override
Updates IPv4 lease.
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
virtual size_t wipeLeases4(const SubnetID &subnet_id) override
Removes specified IPv4 leases.
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:661
virtual Lease4Collection getLeases4() const override
Returns all IPv4 leases.
virtual std::pair< uint32_t, uint32_t > getVersion() const override
Returns backend version.
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
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
virtual void getExpiredLeases6(Lease6Collection &expired_leases, const size_t max_leases) const override
Returns a collection of expired DHCPv6 leases.
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.
const isc::log::MessageID DHCPSRV_MYSQL_NO_TLS
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
virtual size_t wipeLeases6(const SubnetID &subnet_id) override
Removed specified IPv6 leases.
Defines the logger used by the top-level component of kea-lfc.
const isc::log::MessageID DHCPSRV_MYSQL_LEASE_DB_RECONNECT_ATTEMPT_FAILED
virtual bool addLease(const Lease4Ptr &lease) override
Adds an IPv4 lease.
virtual std::string getName() const override
Returns backend name.
static bool invokeDbFailedCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection&#39;s restore failed connectivity callback.
Common MySQL and Lease Data Methods.
virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery6(const SubnetID &first_subnet_id, const SubnetID &last_subnet_id) override
Creates and runs the IPv6 lease stats query for a single subnet.
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:34
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:46
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
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.
virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery4(const SubnetID &subnet_id) override
Creates and runs the IPv4 lease stats query for a single subnet.
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
virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs) override
Deletes all expired-reclaimed DHCPv4 leases.
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
Contains a single row of lease statistical data.
Definition: lease_mgr.h:62
std::string toText() const
Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
Definition: duid.cc:75
Wraps value holding size of the page with leases.
Definition: lease_mgr.h:44
const isc::log::MessageID DHCPSRV_MYSQL_GET_EXPIRED6
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::string ClientClass
Defines a single class name.
Definition: classify.h:42
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const override
Returns existing IPv6 lease for a given IPv6 address.
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 LeaseStatsQueryPtr startLeaseStatsQuery4() override
Creates and runs the IPv4 lease stats query.
virtual void getExpiredLeases4(Lease4Collection &expired_leases, const size_t max_leases) const override
Returns a collection of expired DHCPv4 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:503
virtual void updateLease6(const Lease6Ptr &lease6) override
Updates IPv6 lease.
virtual std::string getDescription() const override
Returns description of the backend.
virtual void commit() override
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
virtual void rollback() override
Rollback Transactions.
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.
virtual Lease6Collection getLeases6() const override
Returns all IPv6 leases.
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.
virtual bool deleteLease(const Lease4Ptr &lease) override
Deletes an IPv4 lease.
std::string getErrorColumns()
Return columns in error.