Kea  1.5.0
mysql_lease_mgr.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2018 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/dhcpsrv_log.h>
14 #include <mysql/mysql_connection.h>
15 
16 #include <boost/array.hpp>
17 #include <boost/static_assert.hpp>
18 #include <mysqld_error.h>
19 
20 #include <iostream>
21 #include <iomanip>
22 #include <limits.h>
23 #include <sstream>
24 #include <string>
25 #include <time.h>
26 
27 using namespace isc;
28 using namespace isc::asiolink;
29 using namespace isc::db;
30 using namespace isc::dhcp;
31 using namespace isc::data;
32 using namespace std;
33 
75 
76 namespace {
77 
82 const size_t HOSTNAME_MAX_LEN = 255;
83 
88 const size_t ADDRESS6_TEXT_MAX_LEN = 39;
89 
91 const size_t USER_CONTEXT_MAX_LEN = 8192;
92 
93 boost::array<TaggedStatement, MySqlLeaseMgr::NUM_STATEMENTS>
95  {MySqlLeaseMgr::DELETE_LEASE4,
96  "DELETE FROM lease4 WHERE address = ?"},
97  {MySqlLeaseMgr::DELETE_LEASE4_STATE_EXPIRED,
98  "DELETE FROM lease4 "
99  "WHERE state = ? AND expire < ?"},
100  {MySqlLeaseMgr::DELETE_LEASE6,
101  "DELETE FROM lease6 WHERE address = ?"},
102  {MySqlLeaseMgr::DELETE_LEASE6_STATE_EXPIRED,
103  "DELETE FROM lease6 "
104  "WHERE state = ? AND expire < ?"},
105  {MySqlLeaseMgr::GET_LEASE4,
106  "SELECT address, hwaddr, client_id, "
107  "valid_lifetime, expire, subnet_id, "
108  "fqdn_fwd, fqdn_rev, hostname, "
109  "state, user_context "
110  "FROM lease4"},
111  {MySqlLeaseMgr::GET_LEASE4_ADDR,
112  "SELECT address, hwaddr, client_id, "
113  "valid_lifetime, expire, subnet_id, "
114  "fqdn_fwd, fqdn_rev, hostname, "
115  "state, user_context "
116  "FROM lease4 "
117  "WHERE address = ?"},
118  {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
119  "SELECT address, hwaddr, client_id, "
120  "valid_lifetime, expire, subnet_id, "
121  "fqdn_fwd, fqdn_rev, hostname, "
122  "state, user_context "
123  "FROM lease4 "
124  "WHERE client_id = ?"},
125  {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
126  "SELECT address, hwaddr, client_id, "
127  "valid_lifetime, expire, subnet_id, "
128  "fqdn_fwd, fqdn_rev, hostname, "
129  "state, user_context "
130  "FROM lease4 "
131  "WHERE client_id = ? AND subnet_id = ?"},
132  {MySqlLeaseMgr::GET_LEASE4_HWADDR,
133  "SELECT address, hwaddr, client_id, "
134  "valid_lifetime, expire, subnet_id, "
135  "fqdn_fwd, fqdn_rev, hostname, "
136  "state, user_context "
137  "FROM lease4 "
138  "WHERE hwaddr = ?"},
139  {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
140  "SELECT address, hwaddr, client_id, "
141  "valid_lifetime, expire, subnet_id, "
142  "fqdn_fwd, fqdn_rev, hostname, "
143  "state, user_context "
144  "FROM lease4 "
145  "WHERE hwaddr = ? AND subnet_id = ?"},
146  {MySqlLeaseMgr::GET_LEASE4_PAGE,
147  "SELECT address, hwaddr, client_id, "
148  "valid_lifetime, expire, subnet_id, "
149  "fqdn_fwd, fqdn_rev, hostname, "
150  "state, user_context "
151  "FROM lease4 "
152  "WHERE address > ? "
153  "ORDER BY address "
154  "LIMIT ?"},
155  {MySqlLeaseMgr::GET_LEASE4_SUBID,
156  "SELECT address, hwaddr, client_id, "
157  "valid_lifetime, expire, subnet_id, "
158  "fqdn_fwd, fqdn_rev, hostname, "
159  "state, user_context "
160  "FROM lease4 "
161  "WHERE subnet_id = ?"},
162  {MySqlLeaseMgr::GET_LEASE4_EXPIRE,
163  "SELECT address, hwaddr, client_id, "
164  "valid_lifetime, expire, subnet_id, "
165  "fqdn_fwd, fqdn_rev, hostname, "
166  "state, user_context "
167  "FROM lease4 "
168  "WHERE state != ? AND expire < ? "
169  "ORDER BY expire ASC "
170  "LIMIT ?"},
171  {MySqlLeaseMgr::GET_LEASE6,
172  "SELECT address, duid, valid_lifetime, "
173  "expire, subnet_id, pref_lifetime, "
174  "lease_type, iaid, prefix_len, "
175  "fqdn_fwd, fqdn_rev, hostname, "
176  "hwaddr, hwtype, hwaddr_source, "
177  "state, user_context "
178  "FROM lease6"},
179  {MySqlLeaseMgr::GET_LEASE6_ADDR,
180  "SELECT address, duid, valid_lifetime, "
181  "expire, subnet_id, pref_lifetime, "
182  "lease_type, iaid, prefix_len, "
183  "fqdn_fwd, fqdn_rev, hostname, "
184  "hwaddr, hwtype, hwaddr_source, "
185  "state, user_context "
186  "FROM lease6 "
187  "WHERE address = ? AND lease_type = ?"},
188  {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
189  "SELECT address, duid, valid_lifetime, "
190  "expire, subnet_id, pref_lifetime, "
191  "lease_type, iaid, prefix_len, "
192  "fqdn_fwd, fqdn_rev, hostname, "
193  "hwaddr, hwtype, hwaddr_source, "
194  "state, user_context "
195  "FROM lease6 "
196  "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
197  {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
198  "SELECT address, duid, valid_lifetime, "
199  "expire, subnet_id, pref_lifetime, "
200  "lease_type, iaid, prefix_len, "
201  "fqdn_fwd, fqdn_rev, hostname, "
202  "hwaddr, hwtype, hwaddr_source, "
203  "state, user_context "
204  "FROM lease6 "
205  "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
206  "AND lease_type = ?"},
207  {MySqlLeaseMgr::GET_LEASE6_PAGE,
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 address > ? "
216  "ORDER BY address "
217  "LIMIT ?"},
218  {MySqlLeaseMgr::GET_LEASE6_SUBID,
219  "SELECT address, duid, valid_lifetime, "
220  "expire, subnet_id, pref_lifetime, "
221  "lease_type, iaid, prefix_len, "
222  "fqdn_fwd, fqdn_rev, hostname, "
223  "hwaddr, hwtype, hwaddr_source, "
224  "state, user_context "
225  "FROM lease6 "
226  "WHERE subnet_id = ?"},
227  {MySqlLeaseMgr::GET_LEASE6_DUID,
228  "SELECT address, duid, valid_lifetime, "
229  "expire, subnet_id, pref_lifetime, "
230  "lease_type, iaid, prefix_len, "
231  "fqdn_fwd, fqdn_rev, hostname, "
232  "hwaddr, hwtype, hwaddr_source, "
233  "state, user_context "
234  "FROM lease6 "
235  "WHERE duid = ?"},
236  {MySqlLeaseMgr::GET_LEASE6_EXPIRE,
237  "SELECT address, duid, valid_lifetime, "
238  "expire, subnet_id, pref_lifetime, "
239  "lease_type, iaid, prefix_len, "
240  "fqdn_fwd, fqdn_rev, hostname, "
241  "hwaddr, hwtype, hwaddr_source, "
242  "state, user_context "
243  "FROM lease6 "
244  "WHERE state != ? AND expire < ? "
245  "ORDER BY expire ASC "
246  "LIMIT ?"},
247  {MySqlLeaseMgr::INSERT_LEASE4,
248  "INSERT INTO lease4(address, hwaddr, client_id, "
249  "valid_lifetime, expire, subnet_id, "
250  "fqdn_fwd, fqdn_rev, hostname, "
251  "state, user_context) "
252  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
253  {MySqlLeaseMgr::INSERT_LEASE6,
254  "INSERT INTO lease6(address, duid, valid_lifetime, "
255  "expire, subnet_id, pref_lifetime, "
256  "lease_type, iaid, prefix_len, "
257  "fqdn_fwd, fqdn_rev, hostname, "
258  "hwaddr, hwtype, hwaddr_source, "
259  "state, user_context) "
260  "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
261  {MySqlLeaseMgr::UPDATE_LEASE4,
262  "UPDATE lease4 SET address = ?, hwaddr = ?, "
263  "client_id = ?, valid_lifetime = ?, expire = ?, "
264  "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
265  "hostname = ?, "
266  "state = ?, user_context = ? "
267  "WHERE address = ?"},
268  {MySqlLeaseMgr::UPDATE_LEASE6,
269  "UPDATE lease6 SET address = ?, duid = ?, "
270  "valid_lifetime = ?, expire = ?, subnet_id = ?, "
271  "pref_lifetime = ?, lease_type = ?, iaid = ?, "
272  "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
273  "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
274  "state = ?, user_context = ? "
275  "WHERE address = ?"},
276  {MySqlLeaseMgr::ALL_LEASE4_STATS,
277  "SELECT subnet_id, state, leases as state_count"
278  " FROM lease4_stat ORDER BY subnet_id, state"},
279 
280  {MySqlLeaseMgr::SUBNET_LEASE4_STATS,
281  "SELECT subnet_id, state, leases as state_count"
282  " FROM lease4_stat "
283  " WHERE subnet_id = ? "
284  " ORDER BY state"},
285 
286  {MySqlLeaseMgr::SUBNET_RANGE_LEASE4_STATS,
287  "SELECT subnet_id, state, leases as state_count"
288  " FROM lease4_stat "
289  " WHERE subnet_id >= ? and subnet_id <= ? "
290  " ORDER BY subnet_id, state"},
291 
292  {MySqlLeaseMgr::ALL_LEASE6_STATS,
293  "SELECT subnet_id, lease_type, state, leases as state_count"
294  " FROM lease6_stat ORDER BY subnet_id, lease_type, state" },
295 
296  {MySqlLeaseMgr::SUBNET_LEASE6_STATS,
297  "SELECT subnet_id, lease_type, state, leases as state_count"
298  " FROM lease6_stat "
299  " WHERE subnet_id = ? "
300  " ORDER BY lease_type, state" },
301 
302  {MySqlLeaseMgr::SUBNET_RANGE_LEASE6_STATS,
303  "SELECT subnet_id, lease_type, state, leases as state_count"
304  " FROM lease6_stat "
305  " WHERE subnet_id >= ? and subnet_id <= ? "
306  " ORDER BY subnet_id, lease_type, state" }
307  }
308 };
309 
310 } // namespace
311 
312 namespace isc {
313 namespace dhcp {
314 
321 
323 public:
335  static void setErrorIndicators(MYSQL_BIND* bind, my_bool* error,
336  size_t count) {
337  for (size_t i = 0; i < count; ++i) {
338  error[i] = MLM_FALSE;
339  bind[i].error = reinterpret_cast<char*>(&error[i]);
340  }
341  }
342 
356  static std::string getColumnsInError(my_bool* error, std::string* names,
357  size_t count) {
358  std::string result = "";
359 
360  // Accumulate list of column names
361  for (size_t i = 0; i < count; ++i) {
362  if (error[i] == MLM_TRUE) {
363  if (!result.empty()) {
364  result += ", ";
365  }
366  result += names[i];
367  }
368  }
369 
370  if (result.empty()) {
371  result = "(None)";
372  }
373 
374  return (result);
375  }
376 };
377 
390 
393  static const size_t LEASE_COLUMNS = 11;
394 
395 public:
400  MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), hwaddr_null_(MLM_FALSE),
401  client_id_length_(0), client_id_null_(MLM_FALSE),
402  subnet_id_(0), valid_lifetime_(0),
403  fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0),
404  state_(0), user_context_length_(0),
405  user_context_null_(MLM_FALSE) {
406  memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
407  memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
408  memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
409  memset(user_context_, 0, sizeof(user_context_));
410  std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
411 
412  // Set the column names (for error messages)
413  columns_[0] = "address";
414  columns_[1] = "hwaddr";
415  columns_[2] = "client_id";
416  columns_[3] = "valid_lifetime";
417  columns_[4] = "expire";
418  columns_[5] = "subnet_id";
419  columns_[6] = "fqdn_fwd";
420  columns_[7] = "fqdn_rev";
421  columns_[8] = "hostname";
422  columns_[9] = "state";
423  columns_[10] = "user_context";
424  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
425  }
426 
436  std::vector<MYSQL_BIND> createBindForSend(const Lease4Ptr& lease) {
437 
438  // Store lease object to ensure it remains valid.
439  lease_ = lease;
440 
441  // Initialize prior to constructing the array of MYSQL_BIND structures.
442  // It sets all fields, including is_null, to zero, so we need to set
443  // is_null only if it should be true. This gives up minor performance
444  // benefit while being safe approach. For improved readability, the
445  // code that explicitly sets is_null is there, but is commented out.
446  memset(bind_, 0, sizeof(bind_));
447 
448  // Set up the structures for the various components of the lease4
449  // structure.
450 
451  try {
452  // address: uint32_t
453  // The address in the Lease structure is an IOAddress object. Convert
454  // this to an integer for storage.
455  addr4_ = lease_->addr_.toUint32();
456  bind_[0].buffer_type = MYSQL_TYPE_LONG;
457  bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
458  bind_[0].is_unsigned = MLM_TRUE;
459  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
460  // reasons, see memset() above
461 
462  // hwaddr: varbinary(20) - hardware/MAC address
463  HWAddrPtr hwaddr = lease_->hwaddr_;
464  if (hwaddr) {
465  hwaddr_ = hwaddr->hwaddr_;
466  hwaddr_length_ = hwaddr->hwaddr_.size();
467 
468  // Make sure that the buffer has at least length of 1, even if
469  // empty HW address is passed. This is required by some of the
470  // MySQL connectors that the buffer is set to non-null value.
471  // Otherwise, null value would be inserted into the database,
472  // rather than empty string.
473  if (hwaddr_.empty()) {
474  hwaddr_.resize(1);
475  }
476 
477  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
478  bind_[1].buffer = reinterpret_cast<char*>(&(hwaddr_[0]));
479  bind_[1].buffer_length = hwaddr_length_;
480  bind_[1].length = &hwaddr_length_;
481  } else {
482  bind_[1].buffer_type = MYSQL_TYPE_NULL;
483  // According to http://dev.mysql.com/doc/refman/5.5/en/
484  // c-api-prepared-statement-data-structures.html, the other
485  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
486  // but let's set them to some sane values in case earlier versions
487  // didn't have that assumption.
488  hwaddr_null_ = MLM_TRUE;
489  bind_[1].buffer = NULL;
490  bind_[1].is_null = &hwaddr_null_;
491  }
492 
493  // client_id: varbinary(128)
494  if (lease_->client_id_) {
495  client_id_ = lease_->client_id_->getClientId();
496  client_id_length_ = client_id_.size();
497 
498  // Make sure that the buffer has at least length of 1, even if
499  // empty client id is passed. This is required by some of the
500  // MySQL connectors that the buffer is set to non-null value.
501  // Otherwise, null value would be inserted into the database,
502  // rather than empty string.
503  if (client_id_.empty()) {
504  client_id_.resize(1);
505  }
506 
507  bind_[2].buffer_type = MYSQL_TYPE_BLOB;
508  bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
509  bind_[2].buffer_length = client_id_length_;
510  bind_[2].length = &client_id_length_;
511  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
512  // reasons, see memset() above
513  } else {
514  bind_[2].buffer_type = MYSQL_TYPE_NULL;
515  // According to http://dev.mysql.com/doc/refman/5.5/en/
516  // c-api-prepared-statement-data-structures.html, the other
517  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
518  // but let's set them to some sane values in case earlier versions
519  // didn't have that assumption.
520  client_id_null_ = MLM_TRUE;
521  bind_[2].buffer = NULL;
522  bind_[2].is_null = &client_id_null_;
523  }
524 
525  // valid lifetime: unsigned int
526  bind_[3].buffer_type = MYSQL_TYPE_LONG;
527  bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
528  bind_[3].is_unsigned = MLM_TRUE;
529  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
530  // reasons, see memset() above
531 
532  // expire: timestamp
533  // The lease structure holds the client last transmission time (cltt_)
534  // For convenience for external tools, this is converted to lease
535  // expiry time (expire). The relationship is given by:
536  //
537  // expire = cltt_ + valid_lft_
538  MySqlConnection::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
539  expire_);
540  bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
541  bind_[4].buffer = reinterpret_cast<char*>(&expire_);
542  bind_[4].buffer_length = sizeof(expire_);
543  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
544  // reasons, see memset() above
545 
546  // subnet_id: unsigned int
547  // Can use lease_->subnet_id_ directly as it is of type uint32_t.
548  bind_[5].buffer_type = MYSQL_TYPE_LONG;
549  bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
550  bind_[5].is_unsigned = MLM_TRUE;
551  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
552  // reasons, see memset() above
553 
554  // fqdn_fwd: boolean
555  bind_[6].buffer_type = MYSQL_TYPE_TINY;
556  bind_[6].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
557  bind_[6].is_unsigned = MLM_TRUE;
558  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
559  // reasons, see memset() above
560 
561  // fqdn_rev: boolean
562  bind_[7].buffer_type = MYSQL_TYPE_TINY;
563  bind_[7].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
564  bind_[7].is_unsigned = MLM_TRUE;
565  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
566  // reasons, see memset() above
567 
568  // hostname: varchar(255)
569  // Note that previously we used MYSQL_TYPE_VARCHAR instead of
570  // MYSQL_TYPE_STRING. However, that caused 'buffer type not supported'
571  // errors on some systems running MariaDB.
572  bind_[8].buffer_type = MYSQL_TYPE_STRING;
573  bind_[8].buffer = const_cast<char*>(lease_->hostname_.c_str());
574  bind_[8].buffer_length = lease_->hostname_.length();
575  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
576  // reasons, see memset() above
577 
578  // state: uint32_t.
579  bind_[9].buffer_type = MYSQL_TYPE_LONG;
580  bind_[9].buffer = reinterpret_cast<char*>(&lease_->state_);
581  bind_[9].is_unsigned = MLM_TRUE;
582  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
583  // reasons, see memset() above
584 
585  // user_context: text
586  ConstElementPtr ctx = lease->getContext();
587  if (ctx) {
588  bind_[10].buffer_type = MYSQL_TYPE_STRING;
589  string ctx_txt = ctx->str();
590  strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
591  bind_[10].buffer = user_context_;
592  bind_[10].buffer_length = ctx_txt.length();
593  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
594  // reasons, see memset() above
595  } else {
596  bind_[10].buffer_type = MYSQL_TYPE_NULL;
597  }
598 
599  // Add the error flags
600  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
601 
602  // .. and check that we have the numbers correct at compile time.
603  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
604 
605  } catch (const std::exception& ex) {
607  "Could not create bind array from Lease4: "
608  << lease_->addr_.toText() << ", reason: " << ex.what());
609  }
610 
611  // Add the data to the vector. Note the end element is one after the
612  // end of the array.
613  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
614  }
615 
622  std::vector<MYSQL_BIND> createBindForReceive() {
623 
624  // Initialize MYSQL_BIND array.
625  // It sets all fields, including is_null, to zero, so we need to set
626  // is_null only if it should be true. This gives up minor performance
627  // benefit while being safe approach. For improved readability, the
628  // code that explicitly sets is_null is there, but is commented out.
629  memset(bind_, 0, sizeof(bind_));
630 
631  // address: uint32_t
632  bind_[0].buffer_type = MYSQL_TYPE_LONG;
633  bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
634  bind_[0].is_unsigned = MLM_TRUE;
635  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
636  // reasons, see memset() above
637 
638  // hwaddr: varbinary(20)
639  hwaddr_length_ = sizeof(hwaddr_buffer_);
640  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
641  bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
642  bind_[1].buffer_length = hwaddr_length_;
643  bind_[1].length = &hwaddr_length_;
644  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
645  // reasons, see memset() above
646 
647  // client_id: varbinary(128)
648  client_id_length_ = sizeof(client_id_buffer_);
649  bind_[2].buffer_type = MYSQL_TYPE_BLOB;
650  bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
651  bind_[2].buffer_length = client_id_length_;
652  bind_[2].length = &client_id_length_;
653  bind_[2].is_null = &client_id_null_;
654  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
655  // reasons, see memset() above
656 
657  // lease_time: unsigned int
658  bind_[3].buffer_type = MYSQL_TYPE_LONG;
659  bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
660  bind_[3].is_unsigned = MLM_TRUE;
661  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
662  // reasons, see memset() above
663 
664  // expire: timestamp
665  bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
666  bind_[4].buffer = reinterpret_cast<char*>(&expire_);
667  bind_[4].buffer_length = sizeof(expire_);
668  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
669  // reasons, see memset() above
670 
671  // subnet_id: unsigned int
672  bind_[5].buffer_type = MYSQL_TYPE_LONG;
673  bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
674  bind_[5].is_unsigned = MLM_TRUE;
675  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
676  // reasons, see memset() above
677 
678  // fqdn_fwd: boolean
679  bind_[6].buffer_type = MYSQL_TYPE_TINY;
680  bind_[6].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
681  bind_[6].is_unsigned = MLM_TRUE;
682  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
683  // reasons, see memset() above
684 
685  // fqdn_rev: boolean
686  bind_[7].buffer_type = MYSQL_TYPE_TINY;
687  bind_[7].buffer = reinterpret_cast<char*>(&fqdn_rev_);
688  bind_[7].is_unsigned = MLM_TRUE;
689  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
690  // reasons, see memset() above
691 
692  // hostname: varchar(255)
693  // Note that previously we used MYSQL_TYPE_VARCHAR instead of
694  // MYSQL_TYPE_STRING. However, that caused 'buffer type not supported'
695  // errors on some systems running MariaDB.
696  hostname_length_ = sizeof(hostname_buffer_);
697  bind_[8].buffer_type = MYSQL_TYPE_STRING;
698  bind_[8].buffer = reinterpret_cast<char*>(hostname_buffer_);
699  bind_[8].buffer_length = hostname_length_;
700  bind_[8].length = &hostname_length_;
701  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
702  // reasons, see memset() above
703 
704  // state: uint32_t
705  bind_[9].buffer_type = MYSQL_TYPE_LONG;
706  bind_[9].buffer = reinterpret_cast<char*>(&state_);
707  bind_[9].is_unsigned = MLM_TRUE;
708  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
709  // reasons, see memset() above
710 
711  // user_context: text null
712  user_context_null_ = MLM_FALSE;
713  user_context_length_ = sizeof(user_context_);
714  bind_[10].buffer_type = MYSQL_TYPE_STRING;
715  bind_[10].buffer = reinterpret_cast<char*>(user_context_);
716  bind_[10].buffer_length = user_context_length_;
717  bind_[10].length= &user_context_length_;
718  bind_[10].is_null = &user_context_null_;
719 
720  // Add the error flags
721  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
722 
723  // .. and check that we have the numbers correct at compile time.
724  BOOST_STATIC_ASSERT(10 < LEASE_COLUMNS);
725 
726  // Add the data to the vector. Note the end element is one after the
727  // end of the array.
728  return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
729  }
730 
740  // Convert times received from the database to times for the lease
741  // structure
742  time_t cltt = 0;
743  MySqlConnection::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
744 
745  if (client_id_null_ == MLM_TRUE) {
746  // There's no client-id, so we pass client-id_length_ set to 0
747  client_id_length_ = 0;
748  }
749 
750  // Hostname is passed to Lease4 as a string object. We have to create
751  // it from the buffer holding hostname and the buffer length.
752  std::string hostname(hostname_buffer_,
753  hostname_buffer_ + hostname_length_);
754 
756  HWAddrPtr hwaddr;
757  if (hwaddr_null_ == MLM_FALSE) {
758  hwaddr.reset(new HWAddr(hwaddr_buffer_, hwaddr_length_, HTYPE_ETHER));
759  }
760 
761  // Convert user_context to string as well.
762  std::string user_context;
763  if (user_context_null_ == MLM_FALSE) {
764  user_context_[user_context_length_] = '\0';
765  user_context.assign(user_context_);
766  }
767 
768  // Set the user context if there is one.
769  ConstElementPtr ctx;
770  if (!user_context.empty()) {
771  ctx = Element::fromJSON(user_context);
772  if (!ctx || (ctx->getType() != Element::map)) {
773  isc_throw(BadValue, "user context '" << user_context
774  << "' is not a JSON map");
775  }
776  }
777 
778  // note that T1 and T2 are not stored
779  Lease4Ptr lease(new Lease4(addr4_, hwaddr,
780  client_id_buffer_, client_id_length_,
781  valid_lifetime_, 0, 0, cltt, subnet_id_,
782  fqdn_fwd_, fqdn_rev_, hostname));
783 
784  // Set state.
785  lease->state_ = state_;
786 
787  if (ctx) {
788  lease->setContext(ctx);
789  }
790 
791  return (lease);
792  }
793 
804  std::string getErrorColumns() {
805  return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
806  }
807 
808 private:
809 
810  // Note: All array lengths are equal to the corresponding variable in the
811  // schema.
812  // Note: Arrays are declared fixed length for speed of creation
813  uint32_t addr4_;
814  MYSQL_BIND bind_[LEASE_COLUMNS];
815  std::string columns_[LEASE_COLUMNS];
816  my_bool error_[LEASE_COLUMNS];
817  std::vector<uint8_t> hwaddr_;
818  uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
819  unsigned long hwaddr_length_;
820  my_bool hwaddr_null_;
821  std::vector<uint8_t> client_id_;
822  uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
823  unsigned long client_id_length_;
824  my_bool client_id_null_;
825  MYSQL_TIME expire_;
826  Lease4Ptr lease_;
827  uint32_t subnet_id_;
828  uint32_t valid_lifetime_;
829  my_bool fqdn_fwd_;
830  my_bool fqdn_rev_;
831  char hostname_buffer_[HOSTNAME_MAX_LEN];
832  unsigned long hostname_length_;
833  uint32_t state_;
834  char user_context_[USER_CONTEXT_MAX_LEN];
835  unsigned long user_context_length_;
836  my_bool user_context_null_;
837 };
838 
851 
854  static const size_t LEASE_COLUMNS = 17;
855 
856 public:
861  MySqlLease6Exchange() : addr6_length_(0), duid_length_(0),
862  iaid_(0), lease_type_(0), prefixlen_(0),
863  pref_lifetime_(0), subnet_id_(0), valid_lifetime_(0),
864  fqdn_fwd_(false), fqdn_rev_(false),
865  hostname_length_(0), hwaddr_length_(0),
866  hwaddr_null_(MLM_FALSE), hwtype_(0), hwaddr_source_(0),
867  state_(0), user_context_length_(0),
868  user_context_null_(MLM_FALSE) {
869  memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
870  memset(duid_buffer_, 0, sizeof(duid_buffer_));
871  memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
872  memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
873  memset(user_context_, 0, sizeof(user_context_));
874  std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
875 
876  // Set the column names (for error messages)
877  columns_[0] = "address";
878  columns_[1] = "duid";
879  columns_[2] = "valid_lifetime";
880  columns_[3] = "expire";
881  columns_[4] = "subnet_id";
882  columns_[5] = "pref_lifetime";
883  columns_[6] = "lease_type";
884  columns_[7] = "iaid";
885  columns_[8] = "prefix_len";
886  columns_[9] = "fqdn_fwd";
887  columns_[10] = "fqdn_rev";
888  columns_[11] = "hostname";
889  columns_[12] = "hwaddr";
890  columns_[13] = "hwtype";
891  columns_[14] = "hwaddr_source";
892  columns_[15] = "state";
893  columns_[16] = "user_context";
894  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
895  }
896 
905  std::vector<MYSQL_BIND> createBindForSend(const Lease6Ptr& lease) {
906  // Store lease object to ensure it remains valid.
907  lease_ = lease;
908 
909  // Ensure bind_ array clear for constructing the MYSQL_BIND structures
910  // for this lease.
911  // It sets all fields, including is_null, to zero, so we need to set
912  // is_null only if it should be true. This gives up minor performance
913  // benefit while being safe approach. For improved readability, the
914  // code that explicitly sets is_null is there, but is commented out.
915  memset(bind_, 0, sizeof(bind_));
916 
917  try {
918  // address: varchar(39)
919  addr6_ = lease_->addr_.toText();
920  addr6_length_ = addr6_.size();
921 
922  // In the following statement, the string is being read. However, the
923  // MySQL C interface does not use "const", so the "buffer" element
924  // is declared as "char*" instead of "const char*". To resolve this,
925  // the "const" is discarded. (Note that the address of addr6_.c_str()
926  // is guaranteed to be valid until the next non-const operation on
927  // addr6_.)
928  //
929  // The const_cast could be avoided by copying the string to a writable
930  // buffer and storing the address of that in the "buffer" element.
931  // However, this introduces a copy operation (with additional overhead)
932  // purely to get round the structures introduced by design of the
933  // MySQL interface (which uses the area pointed to by "buffer" as input
934  // when specifying query parameters and as output when retrieving data).
935  // For that reason, "const_cast" has been used.
936  bind_[0].buffer_type = MYSQL_TYPE_STRING;
937  bind_[0].buffer = const_cast<char*>(addr6_.c_str());
938  bind_[0].buffer_length = addr6_length_;
939  bind_[0].length = &addr6_length_;
940  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
941  // reasons, see memset() above
942 
943  // duid: varchar(128)
944  if (!lease_->duid_) {
945  isc_throw(DbOperationError, "lease6 for address " << addr6_
946  << " is missing mandatory client-id.");
947  }
948  duid_ = lease_->duid_->getDuid();
949  duid_length_ = duid_.size();
950 
951  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
952  bind_[1].buffer = reinterpret_cast<char*>(&(duid_[0]));
953  bind_[1].buffer_length = duid_length_;
954  bind_[1].length = &duid_length_;
955  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
956  // reasons, see memset() above
957 
958  // valid lifetime: unsigned int
959  bind_[2].buffer_type = MYSQL_TYPE_LONG;
960  bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
961  bind_[2].is_unsigned = MLM_TRUE;
962  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
963  // reasons, see memset() above
964 
965  // expire: timestamp
966  // The lease structure holds the client last transmission time (cltt_)
967  // For convenience for external tools, this is converted to lease
969  //
970  // expire = cltt_ + valid_lft_
971  MySqlConnection::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
972  expire_);
973  bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
974  bind_[3].buffer = reinterpret_cast<char*>(&expire_);
975  bind_[3].buffer_length = sizeof(expire_);
976  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
977  // reasons, see memset() above
978 
979  // subnet_id: unsigned int
980  // Can use lease_->subnet_id_ directly as it is of type uint32_t.
981  bind_[4].buffer_type = MYSQL_TYPE_LONG;
982  bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
983  bind_[4].is_unsigned = MLM_TRUE;
984  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
985  // reasons, see memset() above
986 
987  // pref_lifetime: unsigned int
988  // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
989  bind_[5].buffer_type = MYSQL_TYPE_LONG;
990  bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
991  bind_[5].is_unsigned = MLM_TRUE;
992  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
993  // reasons, see memset() above
994 
995  // lease_type: tinyint
996  // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
997  lease_type_ = lease_->type_;
998  bind_[6].buffer_type = MYSQL_TYPE_TINY;
999  bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
1000  bind_[6].is_unsigned = MLM_TRUE;
1001  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
1002  // reasons, see memset() above
1003 
1004  // iaid: unsigned int
1005  // Can use lease_->iaid_ directly as it is of type uint32_t.
1006  bind_[7].buffer_type = MYSQL_TYPE_LONG;
1007  bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
1008  bind_[7].is_unsigned = MLM_TRUE;
1009  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1010  // reasons, see memset() above
1011 
1012  // prefix_len: unsigned tinyint
1013  // Can use lease_->prefixlen_ directly as it is uint32_t.
1014  bind_[8].buffer_type = MYSQL_TYPE_TINY;
1015  bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
1016  bind_[8].is_unsigned = MLM_TRUE;
1017  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
1018  // reasons, see memset() above
1019 
1020  // fqdn_fwd: boolean
1021  bind_[9].buffer_type = MYSQL_TYPE_TINY;
1022  bind_[9].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
1023  bind_[9].is_unsigned = MLM_TRUE;
1024  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1025  // reasons, see memset() above
1026 
1027  // fqdn_rev: boolean
1028  bind_[10].buffer_type = MYSQL_TYPE_TINY;
1029  bind_[10].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
1030  bind_[10].is_unsigned = MLM_TRUE;
1031  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
1032  // reasons, see memset() above
1033 
1034  // hostname: varchar(255)
1035  bind_[11].buffer_type = MYSQL_TYPE_STRING;
1036  bind_[11].buffer = const_cast<char*>(lease_->hostname_.c_str());
1037  bind_[11].buffer_length = lease_->hostname_.length();
1038  // bind_[11].is_null = &MLM_FALSE; // commented out for performance
1039  // reasons, see memset() above
1040 
1041  // hwaddr: varbinary(20) - hardware/MAC address
1042  HWAddrPtr hwaddr = lease_->hwaddr_;
1043  if (hwaddr) {
1044  hwaddr_ = hwaddr->hwaddr_;
1045  hwaddr_length_ = hwaddr->hwaddr_.size();
1046 
1047  // Make sure that the buffer has at least length of 1, even if
1048  // empty HW address is passed. This is required by some of the
1049  // MySQL connectors that the buffer is set to non-null value.
1050  // Otherwise, null value would be inserted into the database,
1051  // rather than empty string.
1052  if (hwaddr_.empty()) {
1053  hwaddr_.resize(1);
1054  }
1055 
1056  bind_[12].buffer_type = MYSQL_TYPE_BLOB;
1057  bind_[12].buffer = reinterpret_cast<char*>(&(hwaddr_[0]));
1058  bind_[12].buffer_length = hwaddr_length_;
1059  bind_[12].length = &hwaddr_length_;
1060  } else {
1061  bind_[12].buffer_type = MYSQL_TYPE_NULL;
1062  // According to http://dev.mysql.com/doc/refman/5.5/en/
1063  // c-api-prepared-statement-data-structures.html, the other
1064  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1065  // but let's set them to some sane values in case earlier versions
1066  // didn't have that assumption.
1067  hwaddr_null_ = MLM_TRUE;
1068  bind_[12].buffer = NULL;
1069  bind_[12].is_null = &hwaddr_null_;
1070  }
1071 
1072  // hwtype
1073  if (hwaddr) {
1074  hwtype_ = lease->hwaddr_->htype_;
1075  bind_[13].buffer_type = MYSQL_TYPE_SHORT;
1076  bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
1077  bind_[13].is_unsigned = MLM_TRUE;
1078  } else {
1079  hwtype_ = 0;
1080  bind_[13].buffer_type = MYSQL_TYPE_NULL;
1081  // According to http://dev.mysql.com/doc/refman/5.5/en/
1082  // c-api-prepared-statement-data-structures.html, the other
1083  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1084  // but let's set them to some sane values in case earlier versions
1085  // didn't have that assumption.
1086  hwaddr_null_ = MLM_TRUE;
1087  bind_[13].buffer = NULL;
1088  bind_[13].is_null = &hwaddr_null_;
1089  }
1090 
1092  if (hwaddr) {
1093  hwaddr_source_ = lease->hwaddr_->source_;
1094  bind_[14].buffer_type = MYSQL_TYPE_LONG;
1095  bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
1096  bind_[14].is_unsigned = MLM_TRUE;
1097  } else {
1098  hwaddr_source_ = 0;
1099  bind_[14].buffer_type = MYSQL_TYPE_NULL;
1100  // According to http://dev.mysql.com/doc/refman/5.5/en/
1101  // c-api-prepared-statement-data-structures.html, the other
1102  // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
1103  // but let's set them to some sane values in case earlier versions
1104  // didn't have that assumption.
1105  hwaddr_null_ = MLM_TRUE;
1106  bind_[14].buffer = NULL;
1107  bind_[14].is_null = &hwaddr_null_;
1108  }
1109 
1110  // state: uint32_t
1111  bind_[15].buffer_type = MYSQL_TYPE_LONG;
1112  bind_[15].buffer = reinterpret_cast<char*>(&lease_->state_);
1113  bind_[15].is_unsigned = MLM_TRUE;
1114  // bind_[15].is_null = &MLM_FALSE; // commented out for performance
1115  // reasons, see memset() above
1116 
1117  // user_context: text
1118  ConstElementPtr ctx = lease->getContext();
1119  if (ctx) {
1120  bind_[16].buffer_type = MYSQL_TYPE_STRING;
1121  string ctx_txt = ctx->str();
1122  strncpy(user_context_, ctx_txt.c_str(), USER_CONTEXT_MAX_LEN - 1);
1123  bind_[16].buffer = user_context_;
1124  bind_[16].buffer_length = ctx_txt.length();
1125  // bind_[16].is_null = &MLM_FALSE; // commented out for performance
1126  // reasons, see memset() above
1127  } else {
1128  bind_[16].buffer_type = MYSQL_TYPE_NULL;
1129  }
1130 
1131  // Add the error flags
1132  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
1133 
1134  // .. and check that we have the numbers correct at compile time.
1135  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
1136 
1137  } catch (const std::exception& ex) {
1139  "Could not create bind array from Lease6: "
1140  << lease_->addr_.toText() << ", reason: " << ex.what());
1141  }
1142 
1143  // Add the data to the vector. Note the end element is one after the
1144  // end of the array.
1145  return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
1146  }
1147 
1156  std::vector<MYSQL_BIND> createBindForReceive() {
1157 
1158  // Initialize MYSQL_BIND array.
1159  // It sets all fields, including is_null, to zero, so we need to set
1160  // is_null only if it should be true. This gives up minor performance
1161  // benefit while being safe approach. For improved readability, the
1162  // code that explicitly sets is_null is there, but is commented out.
1163  memset(bind_, 0, sizeof(bind_));
1164 
1165  // address: varchar(39)
1166  // A Lease6_ address has a maximum of 39 characters. The array is
1167  // one byte longer than this to guarantee that we can always null
1168  // terminate it whatever is returned.
1169  addr6_length_ = sizeof(addr6_buffer_) - 1;
1170  bind_[0].buffer_type = MYSQL_TYPE_STRING;
1171  bind_[0].buffer = addr6_buffer_;
1172  bind_[0].buffer_length = addr6_length_;
1173  bind_[0].length = &addr6_length_;
1174  // bind_[0].is_null = &MLM_FALSE; // commented out for performance
1175  // reasons, see memset() above
1176 
1177  // client_id: varbinary(128)
1178  duid_length_ = sizeof(duid_buffer_);
1179  bind_[1].buffer_type = MYSQL_TYPE_BLOB;
1180  bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
1181  bind_[1].buffer_length = duid_length_;
1182  bind_[1].length = &duid_length_;
1183  // bind_[1].is_null = &MLM_FALSE; // commented out for performance
1184  // reasons, see memset() above
1185 
1186  // lease_time: unsigned int
1187  bind_[2].buffer_type = MYSQL_TYPE_LONG;
1188  bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
1189  bind_[2].is_unsigned = MLM_TRUE;
1190  // bind_[2].is_null = &MLM_FALSE; // commented out for performance
1191  // reasons, see memset() above
1192 
1193  // expire: timestamp
1194  bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
1195  bind_[3].buffer = reinterpret_cast<char*>(&expire_);
1196  bind_[3].buffer_length = sizeof(expire_);
1197  // bind_[3].is_null = &MLM_FALSE; // commented out for performance
1198  // reasons, see memset() above
1199 
1200  // subnet_id: unsigned int
1201  bind_[4].buffer_type = MYSQL_TYPE_LONG;
1202  bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
1203  bind_[4].is_unsigned = MLM_TRUE;
1204  // bind_[4].is_null = &MLM_FALSE; // commented out for performance
1205  // reasons, see memset() above
1206 
1207  // pref_lifetime: unsigned int
1208  bind_[5].buffer_type = MYSQL_TYPE_LONG;
1209  bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
1210  bind_[5].is_unsigned = MLM_TRUE;
1211  // bind_[5].is_null = &MLM_FALSE; // commented out for performance
1212  // reasons, see memset() above
1213 
1214  // lease_type: tinyint
1215  bind_[6].buffer_type = MYSQL_TYPE_TINY;
1216  bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
1217  bind_[6].is_unsigned = MLM_TRUE;
1218  // bind_[6].is_null = &MLM_FALSE; // commented out for performance
1219  // reasons, see memset() above
1220 
1221  // iaid: unsigned int
1222  bind_[7].buffer_type = MYSQL_TYPE_LONG;
1223  bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
1224  bind_[7].is_unsigned = MLM_TRUE;
1225  // bind_[7].is_null = &MLM_FALSE; // commented out for performance
1226  // reasons, see memset() above
1227 
1228  // prefix_len: unsigned tinyint
1229  bind_[8].buffer_type = MYSQL_TYPE_TINY;
1230  bind_[8].buffer = reinterpret_cast<char*>(&prefixlen_);
1231  bind_[8].is_unsigned = MLM_TRUE;
1232  // bind_[8].is_null = &MLM_FALSE; // commented out for performance
1233  // reasons, see memset() above
1234 
1235  // fqdn_fwd: boolean
1236  bind_[9].buffer_type = MYSQL_TYPE_TINY;
1237  bind_[9].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
1238  bind_[9].is_unsigned = MLM_TRUE;
1239  // bind_[9].is_null = &MLM_FALSE; // commented out for performance
1240  // reasons, see memset() above
1241 
1242  // fqdn_rev: boolean
1243  bind_[10].buffer_type = MYSQL_TYPE_TINY;
1244  bind_[10].buffer = reinterpret_cast<char*>(&fqdn_rev_);
1245  bind_[10].is_unsigned = MLM_TRUE;
1246  // bind_[10].is_null = &MLM_FALSE; // commented out for performance
1247  // reasons, see memset() above
1248 
1249  // hostname: varchar(255)
1250  hostname_length_ = sizeof(hostname_buffer_);
1251  bind_[11].buffer_type = MYSQL_TYPE_STRING;
1252  bind_[11].buffer = reinterpret_cast<char*>(hostname_buffer_);
1253  bind_[11].buffer_length = hostname_length_;
1254  bind_[11].length = &hostname_length_;
1255  // bind_[11].is_null = &MLM_FALSE; // commented out for performance
1256  // reasons, see memset() above
1257 
1258  // hwaddr: varbinary(20)
1259  hwaddr_null_ = MLM_FALSE;
1260  hwaddr_length_ = sizeof(hwaddr_buffer_);
1261  bind_[12].buffer_type = MYSQL_TYPE_BLOB;
1262  bind_[12].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
1263  bind_[12].buffer_length = hwaddr_length_;
1264  bind_[12].length = &hwaddr_length_;
1265  bind_[12].is_null = &hwaddr_null_;
1266 
1267  // hardware type: unsigned short int (16 bits)
1268  bind_[13].buffer_type = MYSQL_TYPE_SHORT;
1269  bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
1270  bind_[13].is_unsigned = MLM_TRUE;
1271 
1272  // hardware source: unsigned int (32 bits)
1273  bind_[14].buffer_type = MYSQL_TYPE_LONG;
1274  bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
1275  bind_[14].is_unsigned = MLM_TRUE;
1276 
1277  // state: uint32_t
1278  bind_[15].buffer_type = MYSQL_TYPE_LONG;
1279  bind_[15].buffer = reinterpret_cast<char*>(&state_);
1280  bind_[15].is_unsigned = MLM_TRUE;
1281  // bind_[15].is_null = &MLM_FALSE; // commented out for performance
1282  // reasons, see memset() above
1283 
1284  // user_context: text null
1285  user_context_null_ = MLM_FALSE;
1286  user_context_length_ = sizeof(user_context_);
1287  bind_[16].buffer_type = MYSQL_TYPE_STRING;
1288  bind_[16].buffer = reinterpret_cast<char*>(user_context_);
1289  bind_[16].buffer_length = user_context_length_;
1290  bind_[16].length= &user_context_length_;
1291  bind_[16].is_null = &user_context_null_;
1292 
1293  // Add the error flags
1294  setErrorIndicators(bind_, error_, LEASE_COLUMNS);
1295 
1296  // .. and check that we have the numbers correct at compile time.
1297  BOOST_STATIC_ASSERT(16 < LEASE_COLUMNS);
1298 
1299  // Add the data to the vector. Note the end element is one after the
1300  // end of the array.
1301  return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
1302  }
1303 
1315  // The address buffer is declared larger than the buffer size passed
1316  // to the access function so that we can always append a null byte.
1317  // Create the IOAddress object corresponding to the received data.
1318  addr6_buffer_[addr6_length_] = '\0';
1319  std::string address = addr6_buffer_;
1320  isc::asiolink::IOAddress addr(address);
1321 
1322  // Set the lease type in a variable of the appropriate data type, which
1323  // has been initialized with an arbitrary (but valid) value.
1324  Lease::Type type = Lease::TYPE_NA;
1325  switch (lease_type_) {
1326  case Lease::TYPE_NA:
1327  type = Lease::TYPE_NA;
1328  break;
1329 
1330  case Lease::TYPE_TA:
1331  type = Lease::TYPE_TA;
1332  break;
1333 
1334  case Lease::TYPE_PD:
1335  type = Lease::TYPE_PD;
1336  break;
1337 
1338  default:
1339  isc_throw(BadValue, "invalid lease type returned (" <<
1340  static_cast<int>(lease_type_) << ") for lease with "
1341  << "address " << address << ". Only 0, 1, or 2 are "
1342  << "allowed.");
1343  }
1344 
1345  // Set up DUID,
1346  DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
1347 
1348  // Hostname is passed to Lease6 as a string object, so we have to
1349  // create it from the hostname buffer and length.
1350  std::string hostname(hostname_buffer_,
1351  hostname_buffer_ + hostname_length_);
1352 
1354  HWAddrPtr hwaddr;
1355  if (hwaddr_null_ == MLM_FALSE) {
1356  hwaddr.reset(new HWAddr(hwaddr_buffer_, hwaddr_length_, hwtype_));
1357  hwaddr->source_ = hwaddr_source_;
1358  }
1359 
1360  // Convert user_context to string as well.
1361  std::string user_context;
1362  if (user_context_null_ == MLM_FALSE) {
1363  user_context_[user_context_length_] = '\0';
1364  user_context.assign(user_context_);
1365  }
1366 
1367  // Set the user context if there is one.
1368  ConstElementPtr ctx;
1369  if (!user_context.empty()) {
1370  ctx = Element::fromJSON(user_context);
1371  if (!ctx || (ctx->getType() != Element::map)) {
1372  isc_throw(BadValue, "user context '" << user_context
1373  << "' is not a JSON map");
1374  }
1375  }
1376 
1377  // Create the lease and set the cltt (after converting from the
1378  // expire time retrieved from the database).
1379  Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
1380  pref_lifetime_, valid_lifetime_, 0, 0,
1381  subnet_id_, fqdn_fwd_, fqdn_rev_,
1382  hostname, hwaddr, prefixlen_));
1383  time_t cltt = 0;
1384  MySqlConnection::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
1385  result->cltt_ = cltt;
1386 
1387  // Set state.
1388  result->state_ = state_;
1389 
1390  if (ctx) {
1391  result->setContext(ctx);
1392  }
1393 
1394  return (result);
1395  }
1396 
1407  std::string getErrorColumns() {
1408  return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
1409  }
1410 
1411 private:
1412  // Note: All array lengths are equal to the corresponding variable in the
1413  // schema.
1414  // Note: arrays are declared fixed length for speed of creation
1415  std::string addr6_;
1416  char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];
1417  unsigned long addr6_length_;
1419  MYSQL_BIND bind_[LEASE_COLUMNS];
1420  std::string columns_[LEASE_COLUMNS];
1421  std::vector<uint8_t> duid_;
1422  uint8_t duid_buffer_[DUID::MAX_DUID_LEN];
1423  unsigned long duid_length_;
1424  my_bool error_[LEASE_COLUMNS];
1425  MYSQL_TIME expire_;
1426  uint32_t iaid_;
1427  Lease6Ptr lease_;
1428  uint8_t lease_type_;
1429  uint8_t prefixlen_;
1430  uint32_t pref_lifetime_;
1431  uint32_t subnet_id_;
1432  uint32_t valid_lifetime_;
1433  my_bool fqdn_fwd_;
1434  my_bool fqdn_rev_;
1435  char hostname_buffer_[HOSTNAME_MAX_LEN];
1436  unsigned long hostname_length_;
1437  uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
1438  std::vector<uint8_t> hwaddr_;
1439  unsigned long hwaddr_length_;
1440  my_bool hwaddr_null_;
1441  uint16_t hwtype_;
1442  uint32_t hwaddr_source_;
1443  uint32_t state_;
1444  char user_context_[USER_CONTEXT_MAX_LEN];
1445  unsigned long user_context_length_;
1446  my_bool user_context_null_;
1447 };
1448 
1457 public:
1466  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1467  const bool fetch_type)
1468  : conn_(conn), statement_index_(statement_index), statement_(NULL),
1469  fetch_type_(fetch_type),
1470  // Set the number of columns in the bind array based on fetch_type
1471  // This is the number of columns expected in the result set
1472  bind_(fetch_type_ ? 4 : 3),
1473  subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) {
1474  validateStatement();
1475  }
1476 
1486  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1487  const bool fetch_type, const SubnetID& subnet_id)
1488  : LeaseStatsQuery(subnet_id), conn_(conn), statement_index_(statement_index),
1489  statement_(NULL), fetch_type_(fetch_type),
1490  // Set the number of columns in the bind array based on fetch_type
1491  // This is the number of columns expected in the result set
1492  bind_(fetch_type_ ? 4 : 3),
1493  subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) {
1494  validateStatement();
1495  }
1496 
1509  MySqlLeaseStatsQuery(MySqlConnection& conn, const size_t statement_index,
1510  const bool fetch_type, const SubnetID& first_subnet_id,
1511  const SubnetID& last_subnet_id)
1512  : LeaseStatsQuery(first_subnet_id, last_subnet_id), conn_(conn),
1513  statement_index_(statement_index), statement_(NULL), fetch_type_(fetch_type),
1514  // Set the number of columns in the bind array based on fetch_type
1515  // This is the number of columns expected in the result set
1516  bind_(fetch_type_ ? 4 : 3),
1517  subnet_id_(0), lease_type_(0), lease_state_(0), state_count_(0) {
1518  validateStatement();
1519  }
1520 
1523  (void) mysql_stmt_free_result(statement_);
1524  }
1525 
1533  void start() {
1534  // Set up where clause inputs if needed.
1535  if (getSelectMode() != ALL_SUBNETS) {
1536  MYSQL_BIND inbind[2];
1537  memset(inbind, 0, sizeof(inbind));
1538 
1539  // Add first_subnet_id used by both single and range.
1540  inbind[0].buffer_type = MYSQL_TYPE_LONG;
1541  inbind[0].buffer = reinterpret_cast<char*>(&first_subnet_id_);
1542  inbind[0].is_unsigned = MLM_TRUE;
1543 
1544  // Add last_subnet_id for range.
1545  if (getSelectMode() == SUBNET_RANGE) {
1546  inbind[1].buffer_type = MYSQL_TYPE_LONG;
1547  inbind[1].buffer = reinterpret_cast<char*>(&last_subnet_id_);
1548  inbind[1].is_unsigned = MLM_TRUE;
1549  }
1550 
1551  // Bind the parameters to the statement
1552  int status = mysql_stmt_bind_param(statement_, &inbind[0]);
1553  conn_.checkError(status, statement_index_, "unable to bind parameters");
1554  }
1555 
1556  int col = 0;
1557  // subnet_id: unsigned int
1558  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1559  bind_[col].buffer = reinterpret_cast<char*>(&subnet_id_);
1560  bind_[col].is_unsigned = MLM_TRUE;
1561  ++col;
1562 
1563  // Fetch the lease type if we were told to do so.
1564  if (fetch_type_) {
1565  // lease type: uint32_t
1566  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1567  bind_[col].buffer = reinterpret_cast<char*>(&lease_type_);
1568  bind_[col].is_unsigned = MLM_TRUE;
1569  ++col;
1570  } else {
1571  fetch_type_ = Lease::TYPE_NA;
1572  }
1573 
1574  // state: uint32_t
1575  bind_[col].buffer_type = MYSQL_TYPE_LONG;
1576  bind_[col].buffer = reinterpret_cast<char*>(&lease_state_);
1577  bind_[col].is_unsigned = MLM_TRUE;
1578  ++col;
1579 
1580  // state_count_: uint32_t
1581  bind_[col].buffer_type = MYSQL_TYPE_LONGLONG;
1582  bind_[col].buffer = reinterpret_cast<char*>(&state_count_);
1583  bind_[col].is_unsigned = MLM_TRUE;
1584 
1585  // Set up the MYSQL_BIND array for the data being returned
1586  // and bind it to the statement.
1587  int status = mysql_stmt_bind_result(statement_, &bind_[0]);
1588  conn_.checkError(status, statement_index_, "outbound binding failed");
1589 
1590  // Execute the statement
1591  status = mysql_stmt_execute(statement_);
1592  conn_.checkError(status, statement_index_, "unable to execute");
1593 
1594  // Ensure that all the lease information is retrieved in one go to avoid
1595  // overhead of going back and forth between client and server.
1596  status = mysql_stmt_store_result(statement_);
1597  conn_.checkError(status, statement_index_, "results storage failed");
1598  }
1599 
1612  bool have_row = false;
1613  int status = mysql_stmt_fetch(statement_);
1614  if (status == MLM_MYSQL_FETCH_SUCCESS) {
1615  row.subnet_id_ = static_cast<SubnetID>(subnet_id_);
1616  row.lease_type_ = static_cast<Lease::Type>(lease_type_);
1617  row.lease_state_ = lease_state_;
1618  row.state_count_ = state_count_;
1619  have_row = true;
1620  } else if (status != MYSQL_NO_DATA) {
1621  conn_.checkError(status, statement_index_, "getNextRow failed");
1622  }
1623 
1624  return (have_row);
1625  }
1626 
1627 private:
1631  void validateStatement() {
1632  if (statement_index_ >= MySqlLeaseMgr::NUM_STATEMENTS) {
1633  isc_throw(BadValue, "MySqlLeaseStatsQuery"
1634  " - invalid statement index" << statement_index_);
1635  }
1636 
1637  statement_ = conn_.statements_[statement_index_];
1638  }
1639 
1641  MySqlConnection& conn_;
1642 
1644  size_t statement_index_;
1645 
1647  MYSQL_STMT *statement_;
1648 
1650  bool fetch_type_;
1651 
1653  std::vector<MYSQL_BIND> bind_;
1654 
1656  uint32_t subnet_id_;
1658  uint32_t lease_type_;
1660  uint32_t lease_state_;
1662  int64_t state_count_;
1663 };
1664 
1665 // MySqlLeaseMgr Constructor and Destructor
1666 
1667 MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
1668  : conn_(parameters) {
1669 
1670  // Open the database.
1671  conn_.openDatabase();
1672 
1673  // Test schema version before we try to prepare statements.
1674  std::pair<uint32_t, uint32_t> code_version(MYSQL_SCHEMA_VERSION_MAJOR,
1676  std::pair<uint32_t, uint32_t> db_version = getVersion();
1677  if (code_version != db_version) {
1679  "MySQL schema version mismatch: need version: "
1680  << code_version.first << "." << code_version.second
1681  << " found version: " << db_version.first << "."
1682  << db_version.second);
1683  }
1684 
1685  // Enable autocommit. To avoid a flush to disk on every commit, the global
1686  // parameter innodb_flush_log_at_trx_commit should be set to 2. This will
1687  // cause the changes to be written to the log, but flushed to disk in the
1688  // background every second. Setting the parameter to that value will speed
1689  // up the system, but at the risk of losing data if the system crashes.
1690  my_bool result = mysql_autocommit(conn_.mysql_, 1);
1691  if (result != 0) {
1692  isc_throw(DbOperationError, mysql_error(conn_.mysql_));
1693  }
1694 
1695  // Prepare all statements likely to be used.
1697 
1698  // Create the exchange objects for use in exchanging data between the
1699  // program and the database.
1700  exchange4_.reset(new MySqlLease4Exchange());
1701  exchange6_.reset(new MySqlLease6Exchange());
1702 }
1703 
1705  // There is no need to close the database in this destructor: it is
1706  // closed in the destructor of the mysql_ member variable.
1707 }
1708 
1709 std::string
1711  std::stringstream tmp;
1712  tmp << "MySQL backend " << MYSQL_SCHEMA_VERSION_MAJOR;
1713  tmp << "." << MYSQL_SCHEMA_VERSION_MINOR;
1714  tmp << ", library " << mysql_get_client_info();
1715  return (tmp.str());
1716 }
1717 
1718 // Add leases to the database. The two public methods accept a lease object
1719 // (either V4 of V6), bind the contents to the appropriate prepared
1720 // statement, then call common code to execute the statement.
1721 
1722 bool
1723 MySqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
1724  std::vector<MYSQL_BIND>& bind) {
1725 
1726  // Bind the parameters to the statement
1727  int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
1728  checkError(status, stindex, "unable to bind parameters");
1729 
1730  // Execute the statement
1731  status = mysql_stmt_execute(conn_.statements_[stindex]);
1732  if (status != 0) {
1733 
1734  // Failure: check for the special case of duplicate entry. If this is
1735  // the case, we return false to indicate that the row was not added.
1736  // Otherwise we throw an exception.
1737  if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) {
1738  return (false);
1739  }
1740  checkError(status, stindex, "unable to execute");
1741  }
1742 
1743  // Insert succeeded
1744  return (true);
1745 }
1746 
1747 bool
1750  DHCPSRV_MYSQL_ADD_ADDR4).arg(lease->addr_.toText());
1751 
1752  // Create the MYSQL_BIND array for the lease
1753  std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
1754 
1755  // ... and drop to common code.
1756  return (addLeaseCommon(INSERT_LEASE4, bind));
1757 }
1758 
1759 bool
1762  DHCPSRV_MYSQL_ADD_ADDR6).arg(lease->addr_.toText())
1763  .arg(lease->type_);
1764 
1765  // Create the MYSQL_BIND array for the lease
1766  std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
1767 
1768  // ... and drop to common code.
1769  return (addLeaseCommon(INSERT_LEASE6, bind));
1770 }
1771 
1772 // Extraction of leases from the database.
1773 //
1774 // All getLease() methods ultimately call getLeaseCollection(). This
1775 // binds the input parameters passed to it with the appropriate prepared
1776 // statement and executes the statement. It then gets the results from the
1777 // database. getlease() methods that expect a single result back call it
1778 // with the "single" parameter set true: this causes an exception to be
1779 // generated if multiple records can be retrieved from the result set. (Such
1780 // an occurrence either indicates corruption in the database, or that an
1781 // assumption that a query can only return a single record is incorrect.)
1782 // Methods that require a collection of records have "single" set to the
1783 // default value of false. The logic is the same for both Lease4 and Lease6
1784 // objects, so the code is templated.
1785 //
1786 // Methods that require a collection of objects access this method through
1787 // two interface methods (also called getLeaseCollection()). These are
1788 // short enough as to be defined in the header file: all they do is to supply
1789 // the appropriate MySqlLeaseXExchange object depending on the type of the
1790 // LeaseCollection objects passed to them.
1791 //
1792 // Methods that require a single object to be returned access the method
1793 // through two interface methods (called getLease()). As well as supplying
1794 // the appropriate exchange object, they convert between lease collection
1795 // holding zero or one leases into an appropriate Lease object.
1796 
1797 template <typename Exchange, typename LeaseCollection>
1798 void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
1799  MYSQL_BIND* bind,
1800  Exchange& exchange,
1801  LeaseCollection& result,
1802  bool single) const {
1803 
1804  int status;
1805 
1806  if (bind) {
1807  // Bind the selection parameters to the statement
1808  status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
1809  checkError(status, stindex, "unable to bind WHERE clause parameter");
1810  }
1811 
1812  // Set up the MYSQL_BIND array for the data being returned and bind it to
1813  // the statement.
1814  std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
1815  status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
1816  checkError(status, stindex, "unable to bind SELECT clause parameters");
1817 
1818  // Execute the statement
1819  status = mysql_stmt_execute(conn_.statements_[stindex]);
1820  checkError(status, stindex, "unable to execute");
1821 
1822  // Ensure that all the lease information is retrieved in one go to avoid
1823  // overhead of going back and forth between client and server.
1824  status = mysql_stmt_store_result(conn_.statements_[stindex]);
1825  checkError(status, stindex, "unable to set up for storing all results");
1826 
1827  // Set up the fetch "release" object to release resources associated
1828  // with the call to mysql_stmt_fetch when this method exits, then
1829  // retrieve the data.
1830  MySqlFreeResult fetch_release(conn_.statements_[stindex]);
1831  int count = 0;
1832  while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
1833  try {
1834  result.push_back(exchange->getLeaseData());
1835 
1836  } catch (const isc::BadValue& ex) {
1837  // Rethrow the exception with a bit more data.
1838  isc_throw(BadValue, ex.what() << ". Statement is <" <<
1839  conn_.text_statements_[stindex] << ">");
1840  }
1841 
1842  if (single && (++count > 1)) {
1843  isc_throw(MultipleRecords, "multiple records were found in the "
1844  "database where only one was expected for query "
1845  << conn_.text_statements_[stindex]);
1846  }
1847  }
1848 
1849  // How did the fetch end?
1850  if (status == 1) {
1851  // Error - unable to fetch results
1852  checkError(status, stindex, "unable to fetch results");
1853  } else if (status == MYSQL_DATA_TRUNCATED) {
1854  // Data truncated - throw an exception indicating what was at fault
1855  isc_throw(DataTruncated, conn_.text_statements_[stindex]
1856  << " returned truncated data: columns affected are "
1857  << exchange->getErrorColumns());
1858  }
1859 }
1860 
1861 void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
1862  Lease4Ptr& result) const {
1863  // Create appropriate collection object and get all leases matching
1864  // the selection criteria. The "single" parameter is true to indicate
1865  // that the called method should throw an exception if multiple
1866  // matching records are found: this particular method is called when only
1867  // one or zero matches is expected.
1868  Lease4Collection collection;
1869  getLeaseCollection(stindex, bind, exchange4_, collection, true);
1870 
1871  // Return single record if present, else clear the lease.
1872  if (collection.empty()) {
1873  result.reset();
1874  } else {
1875  result = *collection.begin();
1876  }
1877 }
1878 
1879 void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
1880  Lease6Ptr& result) const {
1881  // Create appropriate collection object and get all leases matching
1882  // the selection criteria. The "single" parameter is true to indicate
1883  // that the called method should throw an exception if multiple
1884  // matching records are found: this particular method is called when only
1885  // one or zero matches is expected.
1886  Lease6Collection collection;
1887  getLeaseCollection(stindex, bind, exchange6_, collection, true);
1888 
1889  // Return single record if present, else clear the lease.
1890  if (collection.empty()) {
1891  result.reset();
1892  } else {
1893  result = *collection.begin();
1894  }
1895 }
1896 
1897 // Basic lease access methods. Obtain leases from the database using various
1898 // criteria.
1899 
1900 Lease4Ptr
1903  DHCPSRV_MYSQL_GET_ADDR4).arg(addr.toText());
1904 
1905  // Set up the WHERE clause value
1906  MYSQL_BIND inbind[1];
1907  memset(inbind, 0, sizeof(inbind));
1908 
1909  uint32_t addr4 = addr.toUint32();
1910  inbind[0].buffer_type = MYSQL_TYPE_LONG;
1911  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
1912  inbind[0].is_unsigned = MLM_TRUE;
1913 
1914  // Get the data
1915  Lease4Ptr result;
1916  getLease(GET_LEASE4_ADDR, inbind, result);
1917 
1918  return (result);
1919 }
1920 
1922 MySqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
1924  DHCPSRV_MYSQL_GET_HWADDR).arg(hwaddr.toText());
1925 
1926  // Set up the WHERE clause value
1927  MYSQL_BIND inbind[1];
1928  memset(inbind, 0, sizeof(inbind));
1929 
1930  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
1931 
1932  unsigned long hwaddr_length = hwaddr.hwaddr_.size();
1933 
1934  // If the data happens to be empty, we have to create a 1 byte dummy
1935  // buffer and pass it to the binding.
1936  uint8_t single_byte_data = 0;
1937 
1938  // As "buffer" is "char*" - even though the data is being read - we need
1939  // to cast away the "const"ness as well as reinterpreting the data as
1940  // a "char*". (We could avoid the "const_cast" by copying the data to a
1941  // local variable, but as the data is only being read, this introduces
1942  // an unnecessary copy).
1943  uint8_t* data = !hwaddr.hwaddr_.empty() ? const_cast<uint8_t*>(&hwaddr.hwaddr_[0])
1944  : &single_byte_data;
1945 
1946  inbind[0].buffer = reinterpret_cast<char*>(data);
1947  inbind[0].buffer_length = hwaddr_length;
1948  inbind[0].length = &hwaddr_length;
1949 
1950  // Get the data
1951  Lease4Collection result;
1952  getLeaseCollection(GET_LEASE4_HWADDR, inbind, result);
1953 
1954  return (result);
1955 }
1956 
1957 Lease4Ptr
1958 MySqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
1960  DHCPSRV_MYSQL_GET_SUBID_HWADDR)
1961  .arg(subnet_id).arg(hwaddr.toText());
1962 
1963  // Set up the WHERE clause value
1964  MYSQL_BIND inbind[2];
1965  memset(inbind, 0, sizeof(inbind));
1966 
1967  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
1968 
1969  unsigned long hwaddr_length = hwaddr.hwaddr_.size();
1970 
1971  // If the data happens to be empty, we have to create a 1 byte dummy
1972  // buffer and pass it to the binding.
1973  std::vector<uint8_t> single_byte_vec(1);
1974 
1975  // As "buffer" is "char*" - even though the data is being read - we need
1976  // to cast away the "const"ness as well as reinterpreting the data as
1977  // a "char*". (We could avoid the "const_cast" by copying the data to a
1978  // local variable, but as the data is only being read, this introduces
1979  // an unnecessary copy).
1980  uint8_t* data = !hwaddr.hwaddr_.empty() ? const_cast<uint8_t*>(&hwaddr.hwaddr_[0])
1981  : &single_byte_vec[0];
1982 
1983  inbind[0].buffer = reinterpret_cast<char*>(data);
1984  inbind[0].buffer_length = hwaddr_length;
1985  inbind[0].length = &hwaddr_length;
1986 
1987  inbind[1].buffer_type = MYSQL_TYPE_LONG;
1988  inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
1989  inbind[1].is_unsigned = MLM_TRUE;
1990 
1991  // Get the data
1992  Lease4Ptr result;
1993  getLease(GET_LEASE4_HWADDR_SUBID, inbind, result);
1994 
1995  return (result);
1996 }
1997 
1999 MySqlLeaseMgr::getLease4(const ClientId& clientid) const {
2001  DHCPSRV_MYSQL_GET_CLIENTID).arg(clientid.toText());
2002 
2003  // Set up the WHERE clause value
2004  MYSQL_BIND inbind[1];
2005  memset(inbind, 0, sizeof(inbind));
2006 
2007  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2008 
2009  std::vector<uint8_t> client_data = clientid.getClientId();
2010  unsigned long client_data_length = client_data.size();
2011 
2012  // If the data happens to be empty, we have to create a 1 byte dummy
2013  // buffer and pass it to the binding.
2014  if (client_data.empty()) {
2015  client_data.resize(1);
2016  }
2017 
2018  inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
2019  inbind[0].buffer_length = client_data_length;
2020  inbind[0].length = &client_data_length;
2021 
2022  // Get the data
2023  Lease4Collection result;
2024  getLeaseCollection(GET_LEASE4_CLIENTID, inbind, result);
2025 
2026  return (result);
2027 }
2028 
2029 Lease4Ptr
2035  isc_throw(NotImplemented, "The MySqlLeaseMgr::getLease4 function was"
2036  " called, but it is not implemented");
2037 }
2038 
2039 Lease4Ptr
2040 MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
2042  DHCPSRV_MYSQL_GET_SUBID_CLIENTID)
2043  .arg(subnet_id).arg(clientid.toText());
2044 
2045  // Set up the WHERE clause value
2046  MYSQL_BIND inbind[2];
2047  memset(inbind, 0, sizeof(inbind));
2048 
2049  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2050 
2051  std::vector<uint8_t> client_data = clientid.getClientId();
2052  unsigned long client_data_length = client_data.size();
2053 
2054  // If the data happens to be empty, we have to create a 1 byte dummy
2055  // buffer and pass it to the binding.
2056  if (client_data.empty()) {
2057  client_data.resize(1);
2058  }
2059 
2060  inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
2061  inbind[0].buffer_length = client_data_length;
2062  inbind[0].length = &client_data_length;
2063 
2064  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2065  inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
2066  inbind[1].is_unsigned = MLM_TRUE;
2067 
2068  // Get the data
2069  Lease4Ptr result;
2070  getLease(GET_LEASE4_CLIENTID_SUBID, inbind, result);
2071 
2072  return (result);
2073 }
2074 
2077  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SUBID4)
2078  .arg(subnet_id);
2079 
2080  // Set up the WHERE clause value
2081  MYSQL_BIND inbind[1];
2082  memset(inbind, 0, sizeof(inbind));
2083 
2084  // Subnet ID
2085  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2086  inbind[0].buffer = reinterpret_cast<char*>(&subnet_id);
2087  inbind[0].is_unsigned = MLM_TRUE;
2088 
2089  // ... and get the data
2090  Lease4Collection result;
2091  getLeaseCollection(GET_LEASE4_SUBID, inbind, result);
2092 
2093  return (result);
2094 }
2095 
2098  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET4);
2099 
2100  Lease4Collection result;
2101  getLeaseCollection(GET_LEASE4, 0, result);
2102 
2103  return (result);
2104 }
2105 
2108  const LeasePageSize& page_size) const {
2109  // Expecting IPv4 address.
2110  if (!lower_bound_address.isV4()) {
2111  isc_throw(InvalidAddressFamily, "expected IPv4 address while "
2112  "retrieving leases from the lease database, got "
2113  << lower_bound_address);
2114  }
2115 
2116  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_PAGE4)
2117  .arg(page_size.page_size_)
2118  .arg(lower_bound_address.toText());
2119 
2120  // Prepare WHERE clause
2121  MYSQL_BIND inbind[2];
2122  memset(inbind, 0, sizeof(inbind));
2123 
2124  // Bind lower bound address
2125  uint32_t lb_address_data = lower_bound_address.toUint32();
2126  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2127  inbind[0].buffer = reinterpret_cast<char*>(&lb_address_data);
2128  inbind[0].is_unsigned = MLM_TRUE;
2129 
2130  // Bind page size value
2131  size_t* ps = const_cast<size_t*>(&page_size.page_size_);
2132  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2133  inbind[1].buffer = reinterpret_cast<char*>(ps);
2134  inbind[1].is_unsigned = MLM_TRUE;
2135 
2136  // Get the leases
2137  Lease4Collection result;
2138  getLeaseCollection(GET_LEASE4_PAGE, inbind, result);
2139 
2140  return (result);
2141 }
2142 
2143 Lease6Ptr
2145  const isc::asiolink::IOAddress& addr) const {
2147  DHCPSRV_MYSQL_GET_ADDR6).arg(addr.toText())
2148  .arg(lease_type);
2149 
2150  // Set up the WHERE clause value
2151  MYSQL_BIND inbind[2];
2152  memset(inbind, 0, sizeof(inbind));
2153 
2154  std::string addr6 = addr.toText();
2155  unsigned long addr6_length = addr6.size();
2156 
2157  // See the earlier description of the use of "const_cast" when accessing
2158  // the address for an explanation of the reason.
2159  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2160  inbind[0].buffer = const_cast<char*>(addr6.c_str());
2161  inbind[0].buffer_length = addr6_length;
2162  inbind[0].length = &addr6_length;
2163 
2164  // LEASE_TYPE
2165  inbind[1].buffer_type = MYSQL_TYPE_TINY;
2166  inbind[1].buffer = reinterpret_cast<char*>(&lease_type);
2167  inbind[1].is_unsigned = MLM_TRUE;
2168 
2169  Lease6Ptr result;
2170  getLease(GET_LEASE6_ADDR, inbind, result);
2171 
2172  return (result);
2173 }
2174 
2177  const DUID& duid, uint32_t iaid) const {
2179  DHCPSRV_MYSQL_GET_IAID_DUID).arg(iaid).arg(duid.toText())
2180  .arg(lease_type);
2181 
2182  // Set up the WHERE clause value
2183  MYSQL_BIND inbind[3];
2184  memset(inbind, 0, sizeof(inbind));
2185 
2186  // In the following statement, the DUID is being read. However, the
2187  // MySQL C interface does not use "const", so the "buffer" element
2188  // is declared as "char*" instead of "const char*". To resolve this,
2189  // the "const" is discarded before the uint8_t* is cast to char*.
2190  //
2191  // Note that the const_cast could be avoided by copying the DUID to
2192  // a writable buffer and storing the address of that in the "buffer"
2193  // element. However, this introduces a copy operation (with additional
2194  // overhead) purely to get round the structures introduced by design of
2195  // the MySQL interface (which uses the area pointed to by "buffer" as
2196  // input when specifying query parameters and as output when retrieving
2197  // data). For that reason, "const_cast" has been used.
2198  const vector<uint8_t>& duid_vector = duid.getDuid();
2199  unsigned long duid_length = duid_vector.size();
2200 
2201  // Make sure that the buffer has at least length of 1, even if
2202  // empty client id is passed. This is required by some of the
2203  // MySQL connectors that the buffer is set to non-null value.
2204  // Otherwise, null value would be inserted into the database,
2205  // rather than empty string.
2206  uint8_t single_byte_data = 0;
2207  uint8_t* data = !duid_vector.empty() ? const_cast<uint8_t*>(&duid_vector[0])
2208  : &single_byte_data;
2209 
2210  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2211  inbind[0].buffer = reinterpret_cast<char*>(data);
2212  inbind[0].buffer_length = duid_length;
2213  inbind[0].length = &duid_length;
2214 
2215  // IAID
2216  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2217  inbind[1].buffer = reinterpret_cast<char*>(&iaid);
2218  inbind[1].is_unsigned = MLM_TRUE;
2219 
2220  // LEASE_TYPE
2221  inbind[2].buffer_type = MYSQL_TYPE_TINY;
2222  inbind[2].buffer = reinterpret_cast<char*>(&lease_type);
2223  inbind[2].is_unsigned = MLM_TRUE;
2224 
2225  // ... and get the data
2226  Lease6Collection result;
2227  getLeaseCollection(GET_LEASE6_DUID_IAID, inbind, result);
2228 
2229  return (result);
2230 }
2231 
2234  const DUID& duid, uint32_t iaid,
2235  SubnetID subnet_id) const {
2237  DHCPSRV_MYSQL_GET_IAID_SUBID_DUID)
2238  .arg(iaid).arg(subnet_id).arg(duid.toText())
2239  .arg(lease_type);
2240 
2241  // Set up the WHERE clause value
2242  MYSQL_BIND inbind[4];
2243  memset(inbind, 0, sizeof(inbind));
2244 
2245  // See the earlier description of the use of "const_cast" when accessing
2246  // the DUID for an explanation of the reason.
2247  const vector<uint8_t>& duid_vector = duid.getDuid();
2248  unsigned long duid_length = duid_vector.size();
2249  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2250  inbind[0].buffer = reinterpret_cast<char*>(
2251  const_cast<uint8_t*>(&duid_vector[0]));
2252  inbind[0].buffer_length = duid_length;
2253  inbind[0].length = &duid_length;
2254 
2255  // IAID
2256  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2257  inbind[1].buffer = reinterpret_cast<char*>(&iaid);
2258  inbind[1].is_unsigned = MLM_TRUE;
2259 
2260  // Subnet ID
2261  inbind[2].buffer_type = MYSQL_TYPE_LONG;
2262  inbind[2].buffer = reinterpret_cast<char*>(&subnet_id);
2263  inbind[2].is_unsigned = MLM_TRUE;
2264 
2265  // LEASE_TYPE
2266  inbind[3].buffer_type = MYSQL_TYPE_TINY;
2267  inbind[3].buffer = reinterpret_cast<char*>(&lease_type);
2268  inbind[3].is_unsigned = MLM_TRUE;
2269 
2270  // ... and get the data
2271  Lease6Collection result;
2272  getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, inbind, result);
2273 
2274  return (result);
2275 }
2276 
2279  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_SUBID6)
2280  .arg(subnet_id);
2281 
2282  // Set up the WHERE clause value
2283  MYSQL_BIND inbind[1];
2284  memset(inbind, 0, sizeof(inbind));
2285 
2286  // Subnet ID
2287  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2288  inbind[0].buffer = reinterpret_cast<char*>(&subnet_id);
2289  inbind[0].is_unsigned = MLM_TRUE;
2290 
2291  // ... and get the data
2292  Lease6Collection result;
2293  getLeaseCollection(GET_LEASE6_SUBID, inbind, result);
2294 
2295  return (result);
2296 }
2297 
2300  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET6);
2301 
2302  Lease6Collection result;
2303  getLeaseCollection(GET_LEASE6, 0, result);
2304 
2305  return (result);
2306 }
2307 
2309 MySqlLeaseMgr::getLeases6(const DUID& duid) const {
2310  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_DUID)
2311  .arg(duid.toText());
2312 
2313  // Set up the WHERE clause value
2314  MYSQL_BIND inbind[1];
2315  memset(inbind, 0, sizeof(inbind));
2316 
2317  const vector<uint8_t>& duid_vector = duid.getDuid();
2318  unsigned long duid_length = duid_vector.size();
2319 
2320  inbind[0].buffer_type = MYSQL_TYPE_BLOB;
2321  inbind[0].buffer = reinterpret_cast<char*>(
2322  const_cast<uint8_t*>(&duid_vector[0]));
2323  inbind[0].buffer_length = duid_length;
2324  inbind[0].length = &duid_length;
2325 
2326  Lease6Collection result;
2327 
2328  getLeaseCollection(GET_LEASE6_DUID, inbind, result);
2329 
2330  return result;
2331 }
2332 
2335  const LeasePageSize& page_size) const {
2336  // Expecting IPv6 address.
2337  if (!lower_bound_address.isV6()) {
2338  isc_throw(InvalidAddressFamily, "expected IPv6 address while "
2339  "retrieving leases from the lease database, got "
2340  << lower_bound_address);
2341  }
2342 
2343  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_PAGE6)
2344  .arg(page_size.page_size_)
2345  .arg(lower_bound_address.toText());
2346 
2347  // Prepare WHERE clause
2348  MYSQL_BIND inbind[2];
2349  memset(inbind, 0, sizeof(inbind));
2350 
2351  // In IPv6 we compare addresses represented as strings. The IPv6 zero address
2352  // is ::, so it is greater than any other address. In this special case, we
2353  // just use 0 for comparison which should be lower than any real IPv6 address.
2354  std::string lb_address_data = "0";
2355  if (!lower_bound_address.isV6Zero()) {
2356  lb_address_data = lower_bound_address.toText();
2357  }
2358 
2359  // Bind lower bound address
2360  unsigned long lb_address_data_size = lb_address_data.size();
2361  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2362  inbind[0].buffer = const_cast<char*>(lb_address_data.c_str());
2363  inbind[0].buffer_length = lb_address_data_size;
2364  inbind[0].length = &lb_address_data_size;
2365 
2366  // Bind page size value
2367  size_t* ps = const_cast<size_t*>(&page_size.page_size_);
2368  inbind[1].buffer_type = MYSQL_TYPE_LONG;
2369  inbind[1].buffer = reinterpret_cast<char*>(ps);
2370  inbind[1].is_unsigned = MLM_TRUE;
2371 
2372  // Get the leases
2373  Lease6Collection result;
2374  getLeaseCollection(GET_LEASE6_PAGE, inbind, result);
2375 
2376  return (result);
2377 }
2378 
2379 void
2381  const size_t max_leases) const {
2382  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_EXPIRED4)
2383  .arg(max_leases);
2384  getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
2385 }
2386 
2387 void
2389  const size_t max_leases) const {
2390  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_EXPIRED6)
2391  .arg(max_leases);
2392  getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
2393 }
2394 
2395 template<typename LeaseCollection>
2396 void
2397 MySqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
2398  const size_t max_leases,
2399  StatementIndex statement_index) const {
2400  // Set up the WHERE clause value
2401  MYSQL_BIND inbind[3];
2402  memset(inbind, 0, sizeof(inbind));
2403 
2404  // Exclude reclaimed leases.
2405  uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
2406  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2407  inbind[0].buffer = reinterpret_cast<char*>(&state);
2408  inbind[0].is_unsigned = MLM_TRUE;
2409 
2410  // Expiration timestamp.
2411  MYSQL_TIME expire_time;
2412  conn_.convertToDatabaseTime(time(NULL), expire_time);
2413  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2414  inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
2415  inbind[1].buffer_length = sizeof(expire_time);
2416 
2417  // If the number of leases is 0, we will return all leases. This is
2418  // achieved by setting the limit to a very high value.
2419  uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
2420  std::numeric_limits<uint32_t>::max();
2421  inbind[2].buffer_type = MYSQL_TYPE_LONG;
2422  inbind[2].buffer = reinterpret_cast<char*>(&limit);
2423  inbind[2].is_unsigned = MLM_TRUE;
2424 
2425  // Get the data
2426  getLeaseCollection(statement_index, inbind, expired_leases);
2427 }
2428 
2429 // Update lease methods. These comprise common code that handles the actual
2430 // update, and type-specific methods that set up the parameters for the prepared
2431 // statement depending on the type of lease.
2432 
2433 template <typename LeasePtr>
2434 void
2435 MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
2436  const LeasePtr& lease) {
2437 
2438  // Bind the parameters to the statement
2439  int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
2440  checkError(status, stindex, "unable to bind parameters");
2441 
2442  // Execute
2443  status = mysql_stmt_execute(conn_.statements_[stindex]);
2444  checkError(status, stindex, "unable to execute");
2445 
2446  // See how many rows were affected. The statement should only update a
2447  // single row.
2448  int affected_rows = mysql_stmt_affected_rows(conn_.statements_[stindex]);
2449  if (affected_rows == 0) {
2450  isc_throw(NoSuchLease, "unable to update lease for address " <<
2451  lease->addr_ << " as it does not exist");
2452  } else if (affected_rows > 1) {
2453  // Should not happen - primary key constraint should only have selected
2454  // one row.
2455  isc_throw(DbOperationError, "apparently updated more than one lease "
2456  "that had the address " << lease->addr_);
2457  }
2458 }
2459 
2460 void
2462  const StatementIndex stindex = UPDATE_LEASE4;
2463 
2465  DHCPSRV_MYSQL_UPDATE_ADDR4).arg(lease->addr_.toText());
2466 
2467  // Create the MYSQL_BIND array for the data being updated
2468  std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
2469 
2470  // Set up the WHERE clause and append it to the MYSQL_BIND array
2471  MYSQL_BIND where;
2472  memset(&where, 0, sizeof(where));
2473 
2474  uint32_t addr4 = lease->addr_.toUint32();
2475  where.buffer_type = MYSQL_TYPE_LONG;
2476  where.buffer = reinterpret_cast<char*>(&addr4);
2477  where.is_unsigned = MLM_TRUE;
2478  bind.push_back(where);
2479 
2480  // Drop to common update code
2481  updateLeaseCommon(stindex, &bind[0], lease);
2482 }
2483 
2484 void
2486  const StatementIndex stindex = UPDATE_LEASE6;
2487 
2489  DHCPSRV_MYSQL_UPDATE_ADDR6).arg(lease->addr_.toText())
2490  .arg(lease->type_);
2491 
2492  // Create the MYSQL_BIND array for the data being updated
2493  std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
2494 
2495  // Set up the WHERE clause value
2496  MYSQL_BIND where;
2497  memset(&where, 0, sizeof(where));
2498 
2499  std::string addr6 = lease->addr_.toText();
2500  unsigned long addr6_length = addr6.size();
2501 
2502  // See the earlier description of the use of "const_cast" when accessing
2503  // the address for an explanation of the reason.
2504  where.buffer_type = MYSQL_TYPE_STRING;
2505  where.buffer = const_cast<char*>(addr6.c_str());
2506  where.buffer_length = addr6_length;
2507  where.length = &addr6_length;
2508  bind.push_back(where);
2509 
2510  // Drop to common update code
2511  updateLeaseCommon(stindex, &bind[0], lease);
2512 }
2513 
2514 // Delete lease methods. Similar to other groups of methods, these comprise
2515 // a per-type method that sets up the relevant MYSQL_BIND array (in this
2516 // case, a single method for both V4 and V6 addresses) and a common method that
2517 // handles the common processing.
2518 
2519 uint64_t
2520 MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
2521 
2522  // Bind the input parameters to the statement
2523  int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
2524  checkError(status, stindex, "unable to bind WHERE clause parameter");
2525 
2526  // Execute
2527  status = mysql_stmt_execute(conn_.statements_[stindex]);
2528  checkError(status, stindex, "unable to execute");
2529 
2530  // See how many rows were affected. Note that the statement may delete
2531  // multiple rows.
2532  return (static_cast<uint64_t>(mysql_stmt_affected_rows(conn_.statements_[stindex])));
2533 }
2534 
2535 bool
2538  DHCPSRV_MYSQL_DELETE_ADDR).arg(addr.toText());
2539 
2540  // Set up the WHERE clause value
2541  MYSQL_BIND inbind[1];
2542  memset(inbind, 0, sizeof(inbind));
2543 
2544  if (addr.isV4()) {
2545  uint32_t addr4 = addr.toUint32();
2546 
2547  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2548  inbind[0].buffer = reinterpret_cast<char*>(&addr4);
2549  inbind[0].is_unsigned = MLM_TRUE;
2550 
2551  return (deleteLeaseCommon(DELETE_LEASE4, inbind) > 0);
2552 
2553  } else {
2554  std::string addr6 = addr.toText();
2555  unsigned long addr6_length = addr6.size();
2556 
2557  // See the earlier description of the use of "const_cast" when accessing
2558  // the address for an explanation of the reason.
2559  inbind[0].buffer_type = MYSQL_TYPE_STRING;
2560  inbind[0].buffer = const_cast<char*>(addr6.c_str());
2561  inbind[0].buffer_length = addr6_length;
2562  inbind[0].length = &addr6_length;
2563 
2564  return (deleteLeaseCommon(DELETE_LEASE6, inbind) > 0);
2565  }
2566 }
2567 
2568 uint64_t
2571  DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED4)
2572  .arg(secs);
2573  return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
2574 }
2575 
2576 uint64_t
2579  DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED6)
2580  .arg(secs);
2581  return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
2582 }
2583 
2584 uint64_t
2585 MySqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
2586  StatementIndex statement_index) {
2587  // Set up the WHERE clause value
2588  MYSQL_BIND inbind[2];
2589  memset(inbind, 0, sizeof(inbind));
2590 
2591  // State is reclaimed.
2592  uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
2593  inbind[0].buffer_type = MYSQL_TYPE_LONG;
2594  inbind[0].buffer = reinterpret_cast<char*>(&state);
2595  inbind[0].is_unsigned = MLM_TRUE;
2596 
2597  // Expiration timestamp.
2598  MYSQL_TIME expire_time;
2599  conn_.convertToDatabaseTime(time(NULL) - static_cast<time_t>(secs), expire_time);
2600  inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
2601  inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
2602  inbind[1].buffer_length = sizeof(expire_time);
2603 
2604  // Get the number of deleted leases and log it.
2605  uint64_t deleted_leases = deleteLeaseCommon(statement_index, inbind);
2607  DHCPSRV_MYSQL_DELETED_EXPIRED_RECLAIMED)
2608  .arg(deleted_leases);
2609 
2610  return (deleted_leases);
2611 }
2612 
2615  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_,
2617  false));
2618  query->start();
2619  return(query);
2620 }
2621 
2624  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_,
2626  false,
2627  subnet_id));
2628  query->start();
2629  return(query);
2630 }
2631 
2634  const SubnetID& last_subnet_id) {
2635  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_,
2637  false,
2638  first_subnet_id, last_subnet_id));
2639  query->start();
2640  return(query);
2641 }
2642 
2645  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_,
2647  true));
2648  query->start();
2649  return(query);
2650 }
2651 
2654  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_,
2656  true,
2657  subnet_id));
2658  query->start();
2659  return(query);
2660 }
2661 
2664  const SubnetID& last_subnet_id) {
2665  LeaseStatsQueryPtr query(new MySqlLeaseStatsQuery(conn_,
2667  true,
2668  first_subnet_id, last_subnet_id));
2669  query->start();
2670  return(query);
2671 }
2672 
2673 size_t
2674 MySqlLeaseMgr::wipeLeases4(const SubnetID& /*subnet_id*/) {
2675  isc_throw(NotImplemented, "wipeLeases4 is not implemented for MySQL backend");
2676 }
2677 
2678 size_t
2679 MySqlLeaseMgr::wipeLeases6(const SubnetID& /*subnet_id*/) {
2680  isc_throw(NotImplemented, "wipeLeases6 is not implemented for MySQL backend");
2681 }
2682 
2683 // Miscellaneous database methods.
2684 
2685 std::string
2687  std::string name = "";
2688  try {
2689  name = conn_.getParameter("name");
2690  } catch (...) {
2691  // Return an empty name
2692  }
2693  return (name);
2694 }
2695 
2696 std::string
2698  return (std::string("MySQL Database"));
2699 }
2700 
2701 std::pair<uint32_t, uint32_t>
2704  DHCPSRV_MYSQL_GET_VERSION);
2705 
2706  // Allocate a new statement.
2707  MYSQL_STMT *stmt = mysql_stmt_init(conn_.mysql_);
2708  if (stmt == NULL) {
2709  isc_throw(DbOperationError, "unable to allocate MySQL prepared "
2710  "statement structure, reason: " << mysql_error(conn_.mysql_));
2711  }
2712 
2713  // Prepare the statement from SQL text.
2714  const char* version_sql = "SELECT version, minor FROM schema_version";
2715  int status = mysql_stmt_prepare(stmt, version_sql, strlen(version_sql));
2716  if (status != 0) {
2717  isc_throw(DbOperationError, "unable to prepare MySQL statement <"
2718  << version_sql << ">, reason: " << mysql_error(conn_.mysql_));
2719  }
2720 
2721  // Execute the prepared statement.
2722  if (mysql_stmt_execute(stmt) != 0) {
2723  isc_throw(DbOperationError, "cannot execute schema version query <"
2724  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
2725  }
2726 
2727  // Bind the output of the statement to the appropriate variables.
2728  MYSQL_BIND bind[2];
2729  memset(bind, 0, sizeof(bind));
2730 
2731  uint32_t major;
2732  bind[0].buffer_type = MYSQL_TYPE_LONG;
2733  bind[0].is_unsigned = 1;
2734  bind[0].buffer = &major;
2735  bind[0].buffer_length = sizeof(major);
2736 
2737  uint32_t minor;
2738  bind[1].buffer_type = MYSQL_TYPE_LONG;
2739  bind[1].is_unsigned = 1;
2740  bind[1].buffer = &minor;
2741  bind[1].buffer_length = sizeof(minor);
2742 
2743  if (mysql_stmt_bind_result(stmt, bind)) {
2744  isc_throw(DbOperationError, "unable to bind result set for <"
2745  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
2746  }
2747 
2748  // Fetch the data.
2749  if (mysql_stmt_fetch(stmt)) {
2750  mysql_stmt_close(stmt);
2751  isc_throw(DbOperationError, "unable to bind result set for <"
2752  << version_sql << ">, reason: " << mysql_errno(conn_.mysql_));
2753  }
2754 
2755  // Discard the statement and its resources
2756  mysql_stmt_close(stmt);
2757 
2758  return (std::make_pair(major, minor));
2759 }
2760 
2761 void
2763  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
2764  if (mysql_commit(conn_.mysql_) != 0) {
2765  isc_throw(DbOperationError, "commit failed: " << mysql_error(conn_.mysql_));
2766  }
2767 }
2768 
2769 void
2771  LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
2772  if (mysql_rollback(conn_.mysql_) != 0) {
2773  isc_throw(DbOperationError, "rollback failed: " << mysql_error(conn_.mysql_));
2774  }
2775 }
2776 
2777 void
2778 MySqlLeaseMgr::checkError(int status, StatementIndex index,
2779  const char* what) const {
2780  conn_.checkError(status, index, what);
2781 }
2782 
2783 } // namespace dhcp
2784 } // namespace isc
std::vector< std::string > text_statements_
Raw text of statements.
void start()
Creates the IPv4 lease statistical data result set.
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:20
MySqlHolder mysql_
MySQL connection handle.
virtual std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
boost::shared_ptr< LeaseStatsQuery > LeaseStatsQueryPtr
Defines a pointer to a LeaseStatsQuery.
Definition: lease_mgr.h:207
Fetch and Release MySQL Results.
A generic exception that is thrown when a function is not implemented.
static const uint32_t STATE_EXPIRED_RECLAIMED
Expired and reclaimed lease.
Definition: lease.h:67
const std::vector< uint8_t > & getClientId() const
Returns reference to the client-id data.
Definition: duid.cc:116
Structure that holds a lease for IPv4 address.
Definition: lease.h:256
static std::string getDBVersion()
Local version of getDBVersion() class method.
static void convertToDatabaseTime(const time_t input_time, MYSQL_TIME &output_time)
Convert time_t value to database time.
virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t secs)
Deletes all expired-reclaimed DHCPv6 leases.
virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress &addr) const
Returns an IPv4 lease for specified IPv4 address.
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
Data is truncated.
Definition: db_exceptions.h:35
static void setErrorIndicators(MYSQL_BIND *bind, my_bool *error, size_t count)
Set error indicators.
MySqlLeaseStatsQuery(MySqlConnection &conn, const size_t statement_index, const bool fetch_type, const SubnetID &subnet_id)
Constructor to query for a single subnet's stats.
virtual void getExpiredLeases4(Lease4Collection &expired_leases, const size_t max_leases) const
Returns a collection of expired DHCPv4 leases.
virtual size_t wipeLeases6(const SubnetID &subnet_id)
Removed specified IPv6 leases.
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:455
const uint32_t MYSQL_SCHEMA_VERSION_MAJOR
virtual Lease6Ptr getLease6(Lease::Type type, const isc::asiolink::IOAddress &addr) const
Returns existing IPv6 lease for a given IPv6 address.
virtual void updateLease6(const Lease6Ptr &lease6)
Updates IPv6 lease.
std::vector< uint8_t > hwaddr_
Definition: hwaddr.h:98
Base class for fulfilling a statistical lease data query.
Definition: lease_mgr.h:128
const uint32_t MYSQL_SCHEMA_VERSION_MINOR
virtual LeaseStatsQueryPtr startLeaseStatsQuery4()
Creates and runs the IPv4 lease stats query.
virtual size_t wipeLeases4(const SubnetID &subnet_id)
Removes specified IPv4 leases.
virtual Lease6Collection getLeases6() const
Returns all IPv6 leases.
boost::shared_ptr< Lease > LeasePtr
Pointer to the lease object.
Definition: lease.h:26
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Holds DUID (DHCPv6 Unique Identifier)
Definition: duid.h:27
virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t secs)
Deletes all expired-reclaimed DHCPv4 leases.
Exception thrown on failure to open database.
std::vector< MYSQL_BIND > createBindForReceive()
Create BIND array to receive data.
virtual void rollback()
Rollback Transactions.
virtual bool addLease(const Lease4Ptr &lease)
Adds an IPv4 lease.
TaggedStatementArray tagged_statements
Prepared MySQL statements used by the backend to insert and retrieve hosts from the database.
Multiple lease records found where one expected.
Definition: db_exceptions.h:28
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery6(const SubnetID &subnet_id)
Creates and runs the IPv6 lease stats query for a single subnet.
const my_bool MLM_FALSE
MySQL false value.
std::vector< MYSQL_BIND > createBindForSend(const Lease4Ptr &lease)
Create MYSQL_BIND objects for Lease4 Pointer.
const size_t page_size_
Holds page size.
Definition: lease_mgr.h:53
StatementIndex
Statement Tags.
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition: lease.h:245
bool getNextRow(LeaseStatsRow &row)
Fetches the next row in the result set.
virtual std::string getDescription() const
Returns description of the backend.
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
virtual Lease4Collection getLeases4() const
Returns all IPv4 leases.
SubnetID subnet_id_
The subnet ID to which this data applies.
Definition: lease_mgr.h:114
virtual ~MySqlLeaseMgr()
Destructor (closes database)
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
Structure that holds a lease for IPv6 address and/or prefix.
Definition: lease.h:471
std::vector< MYSQL_BIND > createBindForSend(const Lease6Ptr &lease)
Create MYSQL_BIND objects for Lease6 Pointer.
std::string getParameter(const std::string &name) const
Returns value of a connection parameter.
std::vector< Lease6Ptr > Lease6Collection
A collection of IPv6 leases.
Definition: lease.h:604
virtual void updateLease4(const Lease4Ptr &lease4)
Updates IPv4 lease.
MySql derivation of the statistical lease data query.
virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery6(const SubnetID &first_subnet_id, const SubnetID &last_subnet_id)
Creates and runs the IPv6 lease stats query for a single subnet.
const std::vector< uint8_t > & getDuid() const
Returns a const reference to the actual DUID value.
Definition: duid.cc:44
const int MLM_MYSQL_FETCH_SUCCESS
MySQL fetch success code.
Ethernet 10Mbps.
Definition: dhcp4.h:56
Lease6Ptr getLeaseData()
Copy Received Data into Lease6 Object.
MySqlLeaseStatsQuery(MySqlConnection &conn, const size_t statement_index, const bool fetch_type)
Constructor to query for all subnets' stats.
virtual LeaseStatsQueryPtr startSubnetLeaseStatsQuery4(const SubnetID &subnet_id)
Creates and runs the IPv4 lease stats query for a single subnet.
Invalid address family used as input to Lease Manager.
Definition: db_exceptions.h:64
Defines the logger used by the top-level component of kea-dhcp-ddns.
Common MySQL and Lease Data Methods.
Lease4Ptr getLeaseData()
Copy Received Data into Lease4 Object.
Exchange MySQL and Lease6 Data.
Exchange MySQL and Lease4 Data.
std::vector< MYSQL_STMT * > statements_
Prepared statements.
uint32_t lease_state_
The lease_state to which the count applies.
Definition: lease_mgr.h:118
Type
Type of lease or pool.
Definition: lease.h:38
Holds Client identifier or client IPv4 address.
Definition: duid.h:111
virtual LeaseStatsQueryPtr startSubnetRangeLeaseStatsQuery4(const SubnetID &first_subnet_id, const SubnetID &last_subnet_id)
Creates and runs the IPv4 lease stats query for a single subnet.
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.
#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
std::vector< MYSQL_BIND > createBindForReceive()
Create BIND array to receive data.
int64_t state_count_
state_count The count of leases in the lease state
Definition: lease_mgr.h:120
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
Contains a single row of lease statistical data.
Definition: lease_mgr.h:61
std::string toText() const
Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
Definition: duid.cc:74
virtual LeaseStatsQueryPtr startLeaseStatsQuery6()
Creates and runs the IPv6 lease stats query.
Wraps value holding size of the page with leases.
Definition: lease_mgr.h:43
virtual std::string getName() const
Returns backend name.
Lease::Type lease_type_
The lease_type to which the count applies.
Definition: lease_mgr.h:116
virtual bool deleteLease(const isc::asiolink::IOAddress &addr)
Deletes a lease.
virtual void getExpiredLeases6(Lease6Collection &expired_leases, const size_t max_leases) const
Returns a collection of expired DHCPv6 leases.
void checkError(const int status, const StatementIndex &index, const char *what) const
Check Error and Throw Exception.
virtual ~MySqlLeaseStatsQuery()
Destructor.
void prepareStatements(const TaggedStatement *start_statement, const TaggedStatement *end_statement)
Prepare statements.
void openDatabase()
Open Database.
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition: lease.h:460
virtual void commit()
Commit Transactions.
static std::string getColumnsInError(my_bool *error, std::string *names, size_t count)
Return columns in error.
std::string toText() const
Returns textual representation of a DUID (e.g. 00:01:02:03:ff)
Definition: duid.cc:121
Exception thrown on failure to execute a database function.
const my_bool MLM_TRUE
MySQL true value.
std::string toText(bool include_htype=true) const
Returns textual representation of a hardware address (e.g.
Definition: hwaddr.cc:51
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
Common MySQL Connector Pool.
std::string getErrorColumns()
Return columns in error.