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