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