Kea 2.5.8
pgsql_host_data_source.cc
Go to the documentation of this file.
1// Copyright (C) 2016-2024 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
11#include <dhcp/libdhcp++.h>
12#include <dhcp/option.h>
14#include <dhcp/option_space.h>
16#include <dhcpsrv/cfg_option.h>
17#include <dhcpsrv/cfgmgr.h>
18#include <dhcpsrv/dhcpsrv_log.h>
19#include <dhcpsrv/host_mgr.h>
21#include <dhcpsrv/timer_mgr.h>
22#include <util/buffer.h>
24#include <util/optional.h>
25
26#include <boost/algorithm/string/split.hpp>
27#include <boost/algorithm/string/classification.hpp>
28#include <boost/array.hpp>
29#include <boost/foreach.hpp>
30#include <boost/pointer_cast.hpp>
31#include <boost/static_assert.hpp>
32
33#include <stdint.h>
34
35#include <mutex>
36#include <string>
37
38using namespace isc;
39using namespace isc::asiolink;
40using namespace isc::db;
41using namespace isc::dhcp;
42using namespace isc::util;
43using namespace isc::data;
44using namespace std;
45
46namespace {
47
51const size_t OPTION_VALUE_MAX_LEN = 4096;
52
57const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
58
60const size_t DHCP_IDENTIFIER_MAX_LEN = ClientId::MAX_CLIENT_ID_LEN;
61
88class PgSqlHostExchange : public PgSqlExchange {
89private:
90
95 static const size_t HOST_ID_COL = 0;
96 static const size_t DHCP_IDENTIFIER_COL = 1;
97 static const size_t DHCP_IDENTIFIER_TYPE_COL = 2;
98 static const size_t DHCP4_SUBNET_ID_COL = 3;
99 static const size_t DHCP6_SUBNET_ID_COL = 4;
100 static const size_t IPV4_ADDRESS_COL = 5;
101 static const size_t HOSTNAME_COL = 6;
102 static const size_t DHCP4_CLIENT_CLASSES_COL = 7;
103 static const size_t DHCP6_CLIENT_CLASSES_COL = 8;
104 static const size_t USER_CONTEXT_COL = 9;
105 static const size_t DHCP4_NEXT_SERVER_COL = 10;
106 static const size_t DHCP4_SERVER_HOSTNAME_COL = 11;
107 static const size_t DHCP4_BOOT_FILE_NAME_COL = 12;
108 static const size_t AUTH_KEY_COL = 13;
110 static const size_t HOST_COLUMNS = 14;
111
112public:
113
120 PgSqlHostExchange(const size_t additional_columns_num = 0)
121 : PgSqlExchange(HOST_COLUMNS + additional_columns_num) {
122 // Set the column names for use by this class. This only comprises
123 // names used by the PgSqlHostExchange class. Derived classes will
124 // need to set names for the columns they use. Currently these are
125 // only used for logging purposes.
126 columns_[HOST_ID_COL] = "host_id";
127 columns_[DHCP_IDENTIFIER_COL] = "dhcp_identifier";
128 columns_[DHCP_IDENTIFIER_TYPE_COL] = "dhcp_identifier_type";
129 columns_[DHCP4_SUBNET_ID_COL] = "dhcp4_subnet_id";
130 columns_[DHCP6_SUBNET_ID_COL] = "dhcp6_subnet_id";
131 columns_[IPV4_ADDRESS_COL] = "ipv4_address";
132 columns_[HOSTNAME_COL] = "hostname";
133 columns_[DHCP4_CLIENT_CLASSES_COL] = "dhcp4_client_classes";
134 columns_[DHCP6_CLIENT_CLASSES_COL] = "dhcp6_client_classes";
135 columns_[USER_CONTEXT_COL] = "user_context";
136 columns_[DHCP4_NEXT_SERVER_COL] = "dhcp4_next_server";
137 columns_[DHCP4_SERVER_HOSTNAME_COL] = "dhcp4_server_hostname";
138 columns_[DHCP4_BOOT_FILE_NAME_COL] = "dhcp4_boot_file_name";
139 columns_[AUTH_KEY_COL] = "auth_key";
140
141 BOOST_STATIC_ASSERT(12 < HOST_COLUMNS);
142 };
143
145 virtual ~PgSqlHostExchange() {
146 }
147
153 virtual void clear() {
154 host_.reset();
155 };
156
171 size_t findAvailColumn() const {
172 std::vector<std::string>::const_iterator empty_column =
173 std::find(columns_.begin(), columns_.end(), std::string());
174 return (std::distance(columns_.begin(), empty_column));
175 }
176
181 HostID getHostId(const PgSqlResult& r, int row) {
182 HostID host_id;
183 getColumnValue(r, row, HOST_ID_COL, host_id);
184 return (host_id);
185 }
186
202 PsqlBindArrayPtr createBindForSend(const HostPtr& host, const bool unique_ip) {
203 if (!host) {
204 isc_throw(BadValue, "createBindForSend:: host object is NULL");
205 }
206
207 // Store the host to ensure bound values remain in scope
208 host_ = host;
209
210 // Bind the host data to the array
211 PsqlBindArrayPtr bind_array(new PsqlBindArray());
212 try {
213 // host_id : is auto_incremented skip it
214
215 // dhcp_identifier : BYTEA NOT NULL
216 bind_array->add(host->getIdentifier());
217
218 // dhcp_identifier_type : SMALLINT NOT NULL
219 bind_array->add(host->getIdentifierType());
220
221 // dhcp4_subnet_id : INT NULL
222 if (host->getIPv4SubnetID() == SUBNET_ID_UNUSED) {
223 bind_array->addNull();
224 } else {
225 bind_array->add(host->getIPv4SubnetID());
226 }
227
228 // dhcp6_subnet_id : INT NULL
229 if (host->getIPv6SubnetID() == SUBNET_ID_UNUSED) {
230 bind_array->addNull();
231 } else {
232 bind_array->add(host->getIPv6SubnetID());
233 }
234
235 // ipv4_address : BIGINT NULL
236 bind_array->add((host->getIPv4Reservation()));
237
238 // hostname : VARCHAR(255) NULL
239 bind_array->add(host->getHostname());
240
241 // dhcp4_client_classes : VARCHAR(255) NULL
242 // Override default separator to not include space after comma.
243 bind_array->addTempString(host->getClientClasses4().toText(","));
244
245 // dhcp6_client_classes : VARCHAR(255) NULL
246 bind_array->addTempString(host->getClientClasses6().toText(","));
247
248 // user_context: TEXT NULL
249 ConstElementPtr ctx = host->getContext();
250 if (ctx) {
251 std::string user_context_ = ctx->str();
252 bind_array->addTempString(user_context_);
253 } else {
254 bind_array->addNull();
255 }
256
257 // dhcp4_next_server : BIGINT NULL
258 bind_array->add((host->getNextServer()));
259
260 // dhcp4_server_hostname : VARCHAR(64)
261 bind_array->add(host->getServerHostname());
262
263 // dhcp4_boot_file_name : VARCHAR(128)
264 bind_array->add(host->getBootFileName());
265
266 // add auth keys
267 std::string key = host->getKey().toText();
268 if (key.empty()) {
269 bind_array->addNull();
270 } else {
271 bind_array->addTempString(key);
272 }
273
274 // When checking whether the IP is unique we need to bind the IPv4 address
275 // at the end of the query as it has additional binding for the IPv4
276 // address.
277 if (unique_ip) {
278 bind_array->add(host->getIPv4Reservation()); // ipv4_address
279 bind_array->add(host->getIPv4SubnetID()); // subnet_id
280 }
281
282 } catch (const std::exception& ex) {
283 host_.reset();
285 "Could not create bind array from Host: "
286 << host->getHostname() << ", reason: " << ex.what());
287 }
288
289 return (bind_array);
290 };
291
305 virtual void processRowData(ConstHostCollection& hosts,
306 const PgSqlResult& r, int row) {
307 // Peek at the host id , so we can skip it if we already have it
308 // This lets us avoid constructing a copy of host for each
309 // of its sub-rows (options, etc...)
310 HostID row_host_id = getHostId(r, row);
311
312 // Add new host only if there are no hosts or the host id of the
313 // most recently added host is different than the host id of the
314 // currently processed host.
315 if (hosts.empty() || row_host_id != hosts.back()->getHostId()) {
316 HostPtr host = retrieveHost(r, row, row_host_id);
317 hosts.push_back(host);
318 }
319 }
320
332 HostPtr retrieveHost(const PgSqlResult& r, int row,
333 const HostID& peeked_host_id = 0) {
334
335 // If the caller peeked ahead at the host_id use that, otherwise
336 // read it from the row.
337 HostID host_id = (peeked_host_id ? peeked_host_id : getHostId(r,row));
338
339 // dhcp_identifier : BYTEA NOT NULL
340 uint8_t identifier_value[DHCP_IDENTIFIER_MAX_LEN];
341 size_t identifier_len;
342 convertFromBytea(r, row, DHCP_IDENTIFIER_COL, identifier_value,
343 sizeof(identifier_value), identifier_len);
344
345 // dhcp_identifier_type : SMALLINT NOT NULL
346 uint8_t type;
347 getColumnValue(r, row, DHCP_IDENTIFIER_TYPE_COL, type);
348 if (type > MAX_IDENTIFIER_TYPE) {
349 isc_throw(BadValue, "invalid dhcp identifier type returned: "
350 << static_cast<int>(type));
351 }
352
353 Host::IdentifierType identifier_type =
354 static_cast<Host::IdentifierType>(type);
355
356 // dhcp4_subnet_id : INT NULL
357 uint32_t subnet_id(SUBNET_ID_UNUSED);
358 if (!isColumnNull(r, row, DHCP4_SUBNET_ID_COL)) {
359 getColumnValue(r, row, DHCP4_SUBNET_ID_COL, subnet_id);
360 }
361 SubnetID dhcp4_subnet_id = static_cast<SubnetID>(subnet_id);
362
363 // dhcp6_subnet_id : INT NULL
364 subnet_id = SUBNET_ID_UNUSED;
365 if (!isColumnNull(r, row, DHCP6_SUBNET_ID_COL)) {
366 getColumnValue(r, row, DHCP6_SUBNET_ID_COL, subnet_id);
367 }
368 SubnetID dhcp6_subnet_id = static_cast<SubnetID>(subnet_id);
369
370 // ipv4_address : BIGINT NULL
371 uint32_t addr4(0);
372 if (!isColumnNull(r, row, IPV4_ADDRESS_COL)) {
373 getColumnValue(r, row, IPV4_ADDRESS_COL, addr4);
374 }
375 isc::asiolink::IOAddress ipv4_reservation(addr4);
376
377 // hostname : VARCHAR(255) NULL
378 std::string hostname;
379 if (!isColumnNull(r, row, HOSTNAME_COL)) {
380 getColumnValue(r, row, HOSTNAME_COL, hostname);
381 }
382
383 // dhcp4_client_classes : VARCHAR(255) NULL
384 std::string dhcp4_client_classes;
385 if (!isColumnNull(r, row, DHCP4_CLIENT_CLASSES_COL)) {
386 getColumnValue(r, row, DHCP4_CLIENT_CLASSES_COL, dhcp4_client_classes);
387 }
388
389 // dhcp6_client_classes : VARCHAR(255) NULL
390 std::string dhcp6_client_classes;
391 if (!isColumnNull(r, row, DHCP6_CLIENT_CLASSES_COL)) {
392 getColumnValue(r, row, DHCP6_CLIENT_CLASSES_COL, dhcp6_client_classes);
393 }
394
395 // user_context: TEXT
396 std::string user_context;
397 if (!isColumnNull(r, row, USER_CONTEXT_COL)) {
398 getColumnValue(r, row, USER_CONTEXT_COL, user_context);
399 }
400
401 // dhcp4_next_server : BIGINT NULL
402 uint32_t dhcp4_next_server_as_uint32(0);
403 if (!isColumnNull(r, row, DHCP4_NEXT_SERVER_COL)) {
404 getColumnValue(r, row, DHCP4_NEXT_SERVER_COL, dhcp4_next_server_as_uint32);
405 }
406 isc::asiolink::IOAddress dhcp4_next_server(dhcp4_next_server_as_uint32);
407
408 // dhcp4_server_hostname : VARCHAR(64)
409 std::string dhcp4_server_hostname;
410 if (!isColumnNull(r, row, DHCP4_SERVER_HOSTNAME_COL)) {
411 getColumnValue(r, row, DHCP4_SERVER_HOSTNAME_COL, dhcp4_server_hostname);
412 }
413
414 // dhcp4_boot_file_name : VARCHAR(128)
415 std::string dhcp4_boot_file_name;
416 if (!isColumnNull(r, row, DHCP4_BOOT_FILE_NAME_COL)) {
417 getColumnValue(r, row, DHCP4_BOOT_FILE_NAME_COL, dhcp4_boot_file_name);
418 }
419
420 // auth_key : VARCHAR(16)
421 std::string auth_key;
422 if (!isColumnNull(r, row, AUTH_KEY_COL)) {
423 getColumnValue(r, row, AUTH_KEY_COL, auth_key);
424 }
425
426 // Finally, attempt to create the new host.
427 HostPtr host;
428 try {
429 host.reset(new Host(identifier_value, identifier_len,
430 identifier_type, dhcp4_subnet_id,
431 dhcp6_subnet_id, ipv4_reservation, hostname,
432 dhcp4_client_classes, dhcp6_client_classes,
433 dhcp4_next_server, dhcp4_server_hostname,
434 dhcp4_boot_file_name, AuthKey(auth_key)));
435
436 // Set the user context if there is one.
437 if (!user_context.empty()) {
438 try {
439 ConstElementPtr ctx = Element::fromJSON(user_context);
440 if (!ctx || (ctx->getType() != Element::map)) {
441 isc_throw(BadValue, "user context '" << user_context
442 << "' is not a JSON map");
443 }
444 host->setContext(ctx);
445 } catch (const isc::data::JSONError& ex) {
446 isc_throw(BadValue, "user context '" << user_context
447 << "' is invalid JSON: " << ex.what());
448 }
449 }
450
451 host->setHostId(host_id);
452 } catch (const isc::Exception& ex) {
453 isc_throw(DbOperationError, "Could not create host: " << ex.what());
454 }
455
456 return(host);
457 };
458
459protected:
462 HostPtr host_;
463};
464
474class PgSqlHostWithOptionsExchange : public PgSqlHostExchange {
475private:
476
478 static const size_t OPTION_COLUMNS = 8;
479
494 class OptionProcessor {
495 public:
496
503 OptionProcessor(const Option::Universe& universe,
504 const size_t start_column)
505 : universe_(universe), start_column_(start_column),
506 option_id_index_(start_column), code_index_(start_column_ + 1),
507 value_index_(start_column_ + 2),
508 formatted_value_index_(start_column_ + 3),
509 space_index_(start_column_ + 4),
510 persistent_index_(start_column_ + 5),
511 cancelled_index_(start_column_ + 6),
512 user_context_index_(start_column_ + 7),
513 most_recent_option_id_(0) {
514 }
515
520 void clear() {
521 most_recent_option_id_ = 0;
522 }
523
556 void retrieveOption(const CfgOptionPtr& cfg, const PgSqlResult& r,
557 int row) {
558 // If the option id on this row is NULL, then there's no
559 // option of this type (4/6) on this row to fetch, so bail.
560 if (PgSqlExchange::isColumnNull(r, row, option_id_index_)) {
561 return;
562 }
563
564 // option_id: INT
565 uint64_t option_id;
566 PgSqlExchange::getColumnValue(r, row, option_id_index_, option_id);
567
568 // The row option id must be greater than id if the most recent
569 // option because they are ordered by option id. Otherwise
570 // we assume that we have already processed this option.
571 if (most_recent_option_id_ >= option_id) {
572 return;
573 }
574
575 // Remember current option id as the most recent processed one. We
576 // will be comparing it with option ids in subsequent rows.
577 most_recent_option_id_ = option_id;
578
579 // code: SMALLINT NOT NULL
580 uint16_t code;
581 PgSqlExchange::getColumnValue(r, row, code_index_, code);
582
583 // value: BYTEA
584 uint8_t value[OPTION_VALUE_MAX_LEN];
585 size_t value_len(0);
586 if (!isColumnNull(r, row, value_index_)) {
587 PgSqlExchange::convertFromBytea(r, row, value_index_, value,
588 sizeof(value), value_len);
589 }
590
591 // formatted_value: TEXT
592 std::string formatted_value;
593 if (!isColumnNull(r, row, formatted_value_index_)) {
594 PgSqlExchange::getColumnValue(r, row, formatted_value_index_,
595 formatted_value);
596 }
597
598 // space: VARCHAR(128)
599 std::string space;
600 if (!isColumnNull(r, row, space_index_)) {
601 PgSqlExchange::getColumnValue(r, row, space_index_, space);
602 }
603
604 // If empty or null space provided, use a default top level space.
605 if (space.empty()) {
606 space = (universe_ == Option::V4 ?
608 }
609
610 // persistent: BOOL default false
611 bool persistent;
612 PgSqlExchange::getColumnValue(r, row, persistent_index_,
613 persistent);
614
615 // cancelled: BOOL default false
616 bool cancelled;
617 PgSqlExchange::getColumnValue(r, row, cancelled_index_,
618 cancelled);
619
620 // user_context: TEXT
621 std::string user_context;
622 if (!isColumnNull(r, row, user_context_index_)) {
623 PgSqlExchange::getColumnValue(r, row, user_context_index_,
624 user_context);
625 }
626
627 // Options are held in a binary or textual format in the database.
628 // This is similar to having an option specified in a server
629 // configuration file. Such option is converted to appropriate C++
630 // class, using option definition. Thus, we need to find the
631 // option definition for this option code and option space.
632
633 // If the option space is a standard DHCPv4 or DHCPv6 option space,
634 // this is most likely a standard option, for which we have a
635 // definition created within libdhcp++.
637
638 // Otherwise, we may check if this an option encapsulated within the
639 // vendor space.
640 if (!def && (space != DHCP4_OPTION_SPACE) &&
641 (space != DHCP6_OPTION_SPACE)) {
642 uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
643 if (vendor_id > 0) {
644 def = LibDHCP::getVendorOptionDef(universe_, vendor_id,
645 code);
646 }
647 }
648
649 // In all other cases, we use runtime option definitions, which
650 // should be also registered within the libdhcp++.
651 if (!def) {
652 def = LibDHCP::getRuntimeOptionDef(space, code);
653 }
654
655 // Finish with a last resort option definition.
656 if (!def) {
657 def = LibDHCP::getLastResortOptionDef(space, code);
658 }
659
660 OptionPtr option;
661
662 if (!def) {
663 // If no definition found, we use generic option type.
664 OptionBuffer buf(value, value + value_len);
665 option.reset(new Option(universe_, code, buf.begin(),
666 buf.end()));
667 } else {
668 // The option value may be specified in textual or binary format
669 // in the database. If formatted_value is empty, the binary
670 // format is used. Depending on the format we use a different
671 // variant of the optionFactory function.
672 if (formatted_value.empty()) {
673 OptionBuffer buf(value, value + value_len);
674 option = def->optionFactory(universe_, code, buf.begin(),
675 buf.end());
676 } else {
677 // Spit the value specified in comma separated values
678 // format.
679 std::vector<std::string> split_vec;
680 boost::split(split_vec, formatted_value,
681 boost::is_any_of(","));
682 option = def->optionFactory(universe_, code, split_vec);
683 }
684 }
685
686 OptionDescriptor desc(option, persistent, cancelled,
687 formatted_value);
688
689 // Set the user context if there is one into the option descriptor.
690 if (!user_context.empty()) {
691 try {
692 ConstElementPtr ctx = Element::fromJSON(user_context);
693 if (!ctx || (ctx->getType() != Element::map)) {
694 isc_throw(BadValue, "user context '" << user_context
695 << "' is no a JSON map");
696 }
697 desc.setContext(ctx);
698 } catch (const isc::data::JSONError& ex) {
699 isc_throw(BadValue, "user context '" << user_context
700 << "' is invalid JSON: " << ex.what());
701 }
702 }
703
704 cfg->add(desc, space);
705 }
706
711 void setColumnNames(std::vector<std::string>& columns) {
712 columns[option_id_index_] = "option_id";
713 columns[code_index_] = "code";
714 columns[value_index_] = "value";
715 columns[formatted_value_index_] = "formatted_value";
716 columns[space_index_] = "space";
717 columns[persistent_index_] = "persistent";
718 columns[cancelled_index_] = "cancelled";
719 columns[user_context_index_] = "user_context";
720 }
721
722 private:
724 Option::Universe universe_;
725
727 size_t start_column_;
728
730
732
733
734 size_t option_id_index_;
735
737 size_t code_index_;
738
740 size_t value_index_;
741
743 size_t formatted_value_index_;
744
746 size_t space_index_;
747
749 size_t persistent_index_;
750
752 size_t cancelled_index_;
754
756 size_t user_context_index_;
757
759 uint64_t most_recent_option_id_;
760 };
761
763 typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
764
765public:
766
773 enum FetchedOptions {
774 DHCP4_ONLY,
775 DHCP6_ONLY,
776 DHCP4_AND_DHCP6
777 };
778
787 PgSqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
788 const size_t additional_columns_num = 0)
789 : PgSqlHostExchange(getRequiredColumnsNum(fetched_options)
790 + additional_columns_num),
791 opt_proc4_(), opt_proc6_() {
792
793 // Create option processor for DHCPv4 options, if required.
794 if ((fetched_options == DHCP4_ONLY) ||
795 (fetched_options == DHCP4_AND_DHCP6)) {
796 opt_proc4_.reset(new OptionProcessor(Option::V4,
797 findAvailColumn()));
798 opt_proc4_->setColumnNames(columns_);
799 }
800
801 // Create option processor for DHCPv6 options, if required.
802 if ((fetched_options == DHCP6_ONLY) ||
803 (fetched_options == DHCP4_AND_DHCP6)) {
804 opt_proc6_.reset(new OptionProcessor(Option::V6,
805 findAvailColumn()));
806 opt_proc6_->setColumnNames(columns_);
807 }
808 }
809
815 virtual void clear() {
816 PgSqlHostExchange::clear();
817 if (opt_proc4_) {
818 opt_proc4_->clear();
819 }
820
821 if (opt_proc6_) {
822 opt_proc6_->clear();
823 }
824 }
825
835 virtual void processRowData(ConstHostCollection& hosts,
836 const PgSqlResult& r, int row) {
837 HostPtr current_host;
838 if (hosts.empty()) {
839 // Must be the first one, fetch it.
840 current_host = retrieveHost(r, row);
841 hosts.push_back(current_host);
842 } else {
843 // Peek at the host id so we can skip it if we already have
844 // this host. This lets us avoid retrieving the host needlessly
845 // for each of its sub-rows (options, etc...).
846 HostID row_host_id = getHostId(r, row);
847 current_host = boost::const_pointer_cast<Host>(hosts.back());
848
849 // if the row's host id is greater than the one we've been
850 // working on we're starting a new host, so fetch it.
851 if (row_host_id > current_host->getHostId()) {
852 current_host = retrieveHost(r, row, row_host_id);
853 hosts.push_back(current_host);
854 }
855 }
856
857 // Parse DHCPv4 options if required to do so.
858 if (opt_proc4_) {
859 CfgOptionPtr cfg = current_host->getCfgOption4();
860 opt_proc4_->retrieveOption(cfg, r, row);
861 }
862
863 // Parse DHCPv6 options if required to do so.
864 if (opt_proc6_) {
865 CfgOptionPtr cfg = current_host->getCfgOption6();
866 opt_proc6_->retrieveOption(cfg, r, row);
867 }
868 }
869
870private:
871
883 static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
884 return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
885 OPTION_COLUMNS);
886 }
887
891 OptionProcessorPtr opt_proc4_;
892
896 OptionProcessorPtr opt_proc6_;
897};
898
911class PgSqlHostIPv6Exchange : public PgSqlHostWithOptionsExchange {
912private:
913
915 static const size_t RESERVATION_COLUMNS = 5;
916
917public:
918
923 PgSqlHostIPv6Exchange(const FetchedOptions& fetched_options)
924 : PgSqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
925 reservation_id_index_(findAvailColumn()),
926 address_index_(reservation_id_index_ + 1),
927 prefix_len_index_(reservation_id_index_ + 2),
928 type_index_(reservation_id_index_ + 3),
929 iaid_index_(reservation_id_index_ + 4),
930 most_recent_reservation_id_(0) {
931
932 // Provide names of additional columns returned by the queries.
933 columns_[reservation_id_index_] = "reservation_id";
934 columns_[address_index_] = "address";
935 columns_[prefix_len_index_] = "prefix_len";
936 columns_[type_index_] = "type";
937 columns_[iaid_index_] = "dhcp6_iaid";
938
939 BOOST_STATIC_ASSERT(4 < RESERVATION_COLUMNS);
940 }
941
947 void clear() {
948 PgSqlHostWithOptionsExchange::clear();
949 most_recent_reservation_id_ = 0;
950 }
951
955 uint64_t getReservationId(const PgSqlResult& r, int row) const {
956 uint64_t resv_id = 0;
957 if (!isColumnNull(r, row, reservation_id_index_)) {
958 getColumnValue(r, row, reservation_id_index_, resv_id);
959 }
960
961 return (resv_id);
962 };
963
968 IPv6Resrv retrieveReservation(const PgSqlResult& r, int row) {
969
970 // type: SMALLINT NOT NULL
971 uint16_t tmp;
972 getColumnValue(r, row, type_index_, tmp);
973
974 // Convert it to IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
975 IPv6Resrv::Type resv_type;
976 switch (tmp) {
977 case 0:
978 resv_type = IPv6Resrv::TYPE_NA;
979 break;
980
981 case 2:
982 resv_type = IPv6Resrv::TYPE_PD;
983 break;
984
985 default:
987 "invalid IPv6 reservation type returned: "
988 << tmp << ". Only 0 or 2 are allowed.");
989 }
990
991 // address VARCHAR(39) NOT NULL
992 isc::asiolink::IOAddress address(getIPv6Value(r, row, address_index_));
993
994 // prefix_len: SMALLINT NOT NULL
995 uint16_t prefix_len;
996 getColumnValue(r, row, prefix_len_index_, prefix_len);
997
998 // @todo once we support populating iaid
999 // iaid: INT
1000 // int iaid;
1001 // getColumnValue(r, row, iaid_index_, iaid);
1002
1003 // Create the reservation.
1004 IPv6Resrv reservation(resv_type, IOAddress(address), prefix_len);
1005 return (reservation);
1006 };
1007
1029 virtual void processRowData(ConstHostCollection& hosts,
1030 const PgSqlResult& r, int row) {
1031 // Call parent class to fetch host information and options.
1032 PgSqlHostWithOptionsExchange::processRowData(hosts, r, row);
1033
1034 // Shouldn't happen but just in case
1035 if (hosts.empty()) {
1036 isc_throw(Unexpected, "no host information while retrieving"
1037 " IPv6 reservation");
1038 }
1039
1040 // If we have reservation id we haven't seen yet, retrieve the
1041 // the reservation, adding it to the current host
1042 uint64_t reservation_id = getReservationId(r, row);
1043 if (reservation_id && (reservation_id > most_recent_reservation_id_)) {
1044 HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
1045 host->addReservation(retrieveReservation(r, row));
1046 most_recent_reservation_id_ = reservation_id;
1047 }
1048 }
1049
1050private:
1052
1053
1054 size_t reservation_id_index_;
1055
1057 size_t address_index_;
1058
1060 size_t prefix_len_index_;
1061
1063 size_t type_index_;
1064
1066 size_t iaid_index_;
1067
1069
1071 uint64_t most_recent_reservation_id_;
1072};
1073
1084class PgSqlIPv6ReservationExchange : public PgSqlExchange {
1085private:
1086
1088 static const size_t RESRV_COLUMNS = 6;
1089
1090public:
1091
1095 PgSqlIPv6ReservationExchange()
1096 : PgSqlExchange(RESRV_COLUMNS),
1097 resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
1098 // Set the column names (for error messages)
1099 columns_[0] = "host_id";
1100 columns_[1] = "address";
1101 columns_[2] = "prefix_len";
1102 columns_[3] = "type";
1103 columns_[4] = "dhcp6_iaid";
1104
1105 BOOST_STATIC_ASSERT(5 < RESRV_COLUMNS);
1106 }
1107
1122 PsqlBindArrayPtr createBindForSend(const IPv6Resrv& resv,
1123 const HostID& host_id,
1124 const bool unique_ip) {
1125 // Store the values to ensure they remain valid.
1126 // Technically we don't need this, as currently all the values
1127 // are converted to strings and stored by the bind array.
1128 resv_ = resv;
1129
1130 PsqlBindArrayPtr bind_array(new PsqlBindArray());
1131
1132 try {
1133 // address VARCHAR(39) NOT NULL
1134 bind_array->add(resv.getPrefix());
1135
1136 // prefix_len: SMALLINT NOT NULL
1137 bind_array->add(resv.getPrefixLen());
1138
1139 // type: SMALLINT NOT NULL
1140 // See lease6_types table for values (0 = IA_NA, 2 = IA_PD)
1141 uint16_t type = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1142 bind_array->add(type);
1143
1144 // dhcp6_iaid: INT UNSIGNED
1146 bind_array->addNull();
1147
1148 // host_id: BIGINT NOT NULL
1149 bind_array->add(host_id);
1150
1151 // When checking whether the IP is unique we need to bind the IPv6 address
1152 // and prefix length at the end of the query as it has additional binding
1153 // for the IPv6 address and prefix length.
1154 if (unique_ip) {
1155 bind_array->add(resv.getPrefix()); // address
1156 bind_array->add(resv.getPrefixLen()); // prefix_len
1157 }
1158 } catch (const std::exception& ex) {
1160 "Could not create bind array from IPv6 Reservation: "
1161 << resv_.toText() << ", reason: " << ex.what());
1162 }
1163
1164 return (bind_array);
1165 }
1166
1167private:
1169 IPv6Resrv resv_;
1170};
1171
1175class PgSqlOptionExchange : public PgSqlExchange {
1176private:
1177
1178 static const size_t OPTION_ID_COL = 0;
1179 static const size_t CODE_COL = 1;
1180 static const size_t VALUE_COL = 2;
1181 static const size_t FORMATTED_VALUE_COL = 3;
1182 static const size_t SPACE_COL = 4;
1183 static const size_t PERSISTENT_COL = 5;
1184 static const size_t CANCELLED_COL = 6;
1185 static const size_t USER_CONTEXT_COL = 7;
1186 static const size_t DHCP_SUBNET_ID_COL = 8;
1187 static const size_t HOST_ID_COL = 9;
1189 static const size_t OPTION_COLUMNS = 10;
1190
1191public:
1192
1194 PgSqlOptionExchange()
1195 : PgSqlExchange(OPTION_COLUMNS), value_(),
1196 value_len_(0), option_() {
1197 columns_[OPTION_ID_COL] = "option_id";
1198 columns_[CODE_COL] = "code";
1199 columns_[VALUE_COL] = "value";
1200 columns_[FORMATTED_VALUE_COL] = "formatted_value";
1201 columns_[SPACE_COL] = "space";
1202 columns_[PERSISTENT_COL] = "persistent";
1203 columns_[CANCELLED_COL] = "cancelled";
1204 columns_[USER_CONTEXT_COL] = "user_context";
1205 columns_[DHCP_SUBNET_ID_COL] = "dhcp_subnet_id";
1206 columns_[HOST_ID_COL] = "host_id";
1207
1208 BOOST_STATIC_ASSERT(10 <= OPTION_COLUMNS);
1209 }
1210
1219 PsqlBindArrayPtr createBindForSend(const OptionDescriptor& opt_desc,
1220 const std::string& opt_space,
1221 const HostID& host_id) {
1222 // Hold pointer to the option to make sure it remains valid until
1223 // we complete a query.
1224 option_ = opt_desc.option_;
1225
1226 // Create the bind-array
1227 PsqlBindArrayPtr bind_array(new PsqlBindArray());
1228
1229 try {
1230 // option_id: is auto_incremented so skip it
1231
1232 // code: SMALLINT UNSIGNED NOT NULL
1233 bind_array->add(option_->getType());
1234
1235 // value: BYTEA NULL
1236 if (opt_desc.formatted_value_.empty() &&
1237 (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
1238 // The formatted_value is empty and the option value is
1239 // non-empty so we need to prepare on-wire format for the
1240 // option and store it in the database as a BYTEA.
1241 OutputBuffer buf(opt_desc.option_->len());
1242 opt_desc.option_->pack(buf);
1243 const uint8_t* buf_ptr = buf.getData();
1244 value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
1245 buf_ptr + buf.getLength());
1246 value_len_ = value_.size();
1247 bind_array->add(value_);
1248 } else {
1249 // No value or formatted_value specified. In this case, the
1250 // value BYTEA should be NULL.
1251 bind_array->addNull(PsqlBindArray::BINARY_FMT);
1252 }
1253
1254 // formatted_value: TEXT NULL,
1255 if (!opt_desc.formatted_value_.empty()) {
1256 bind_array->addTempString(opt_desc.formatted_value_);
1257 } else {
1258 bind_array->addNull();
1259 }
1260
1261 // space: VARCHAR(128) NULL
1262 if (!opt_space.empty()) {
1263 bind_array->addTempString(opt_space);
1264 } else {
1265 bind_array->addNull();
1266 }
1267
1268 // persistent: BOOLEAN DEFAULT false
1269 bind_array->add(opt_desc.persistent_);
1270
1271 // cancelled: BOOLEAN DEFAULT false
1272 bind_array->add(opt_desc.cancelled_);
1273
1274 // user_context: TEXT NULL,
1275 ConstElementPtr ctx = opt_desc.getContext();
1276 if (ctx) {
1277 std::string user_context_ = ctx->str();
1278 bind_array->addTempString(user_context_);
1279 } else {
1280 bind_array->addNull();
1281 }
1282
1283 // host_id: INT NULL
1284 if (!host_id) {
1285 isc_throw(BadValue, "host_id cannot be null");
1286 }
1287 bind_array->add(host_id);
1288
1289 } catch (const std::exception& ex) {
1291 "Could not create bind array for inserting DHCP "
1292 "host option: " << option_->toText() << ", reason: "
1293 << ex.what());
1294 }
1295
1296 return (bind_array);
1297 }
1298
1299private:
1300
1302 std::vector<uint8_t> value_;
1303
1305 size_t value_len_;
1306
1308 OptionPtr option_;
1309};
1310
1311} // namespace
1312
1313namespace isc {
1314namespace dhcp {
1315
1326public:
1327
1334 IOServiceAccessorPtr io_service_accessor,
1335 db::DbCallback db_reconnect_callback);
1336
1341
1344 boost::shared_ptr<PgSqlHostWithOptionsExchange> host_ipv4_exchange_;
1345
1348 boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv6_exchange_;
1349
1353 boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv46_exchange_;
1354
1357 boost::shared_ptr<PgSqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
1358
1362 boost::shared_ptr<PgSqlOptionExchange> host_option_exchange_;
1363
1366
1369};
1370
1378public:
1379
1381 std::vector<PgSqlHostContextPtr> pool_;
1382
1384 std::mutex mutex_;
1385};
1386
1388typedef boost::shared_ptr<PgSqlHostContextPool> PgSqlHostContextPoolPtr;
1389
1392public:
1393
1403 GET_HOST_DHCPID, // Gets hosts by host identifier
1404 GET_HOST_ADDR, // Gets hosts by IPv4 address
1405 GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
1406 GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
1407 GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
1408 GET_HOST_PREFIX, // Gets host by IPv6 prefix
1409 GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
1410 GET_HOST_ADDR6, // Gets hosts by IPv6 address/prefix
1411 GET_HOST_SUBID4, // Gets hosts by IPv4 SubnetID
1412 GET_HOST_SUBID6, // Gets hosts by IPv6 SubnetID
1413 GET_HOST_HOSTNAME, // Gets hosts by hostname
1414 GET_HOST_HOSTNAME_SUBID4, // Gets hosts by hostname and IPv4 SubnetID
1415 GET_HOST_HOSTNAME_SUBID6, // Gets hosts by hostname and IPv6 SubnetID
1416 GET_HOST_SUBID4_PAGE, // Gets hosts by IPv4 SubnetID beginning by HID
1417 GET_HOST_SUBID6_PAGE, // Gets hosts by IPv6 SubnetID beginning by HID
1418 GET_HOST_PAGE4, // Gets v4 hosts beginning by HID
1419 GET_HOST_PAGE6, // Gets v6 hosts beginning by HID
1420 INSERT_HOST_NON_UNIQUE_IP, // Insert new host to collection with allowing IP duplicates
1421 INSERT_HOST_UNIQUE_IP, // Insert new host to collection with checking for IP duplicates
1422 INSERT_V6_RESRV_NON_UNIQUE,// Insert v6 reservation without checking that it is unique
1423 INSERT_V6_RESRV_UNIQUE, // Insert v6 reservation with checking that it is unique
1424 INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
1425 INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
1426 DEL_HOST_ADDR4, // Delete v4 host (subnet-id, addr4)
1427 DEL_HOST_ADDR6, // Delete v6 host (subnet-id, addr6)
1428 DEL_HOST_SUBID4_ID, // Delete v4 host (subnet-id, ident.type, identifier)
1429 DEL_HOST_SUBID6_ID, // Delete v6 host (subnet-id, ident.type, identifier)
1430 NUM_STATEMENTS // Number of statements
1432
1439
1445
1448
1471 static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl);
1472
1483
1500 uint64_t addStatement(PgSqlHostContextPtr& ctx,
1502 PsqlBindArrayPtr& bind,
1503 const bool return_last_id = false);
1504
1514 PsqlBindArrayPtr& bind);
1515
1521 void addResv(PgSqlHostContextPtr& ctx,
1522 const IPv6Resrv& resv,
1523 const HostID& id);
1524
1536 const OptionDescriptor& opt_desc,
1537 const std::string& opt_space,
1538 const Optional<SubnetID>& subnet_id,
1539 const HostID& host_id);
1540
1550 const StatementIndex& stindex,
1551 const ConstCfgOptionPtr& options_cfg,
1552 const uint64_t host_id);
1553
1573 StatementIndex stindex,
1574 PsqlBindArrayPtr bind,
1575 boost::shared_ptr<PgSqlHostExchange> exchange,
1576 ConstHostCollection& result,
1577 bool single) const;
1578
1597 const SubnetID& subnet_id,
1598 const Host::IdentifierType& identifier_type,
1599 const uint8_t* identifier_begin,
1600 const size_t identifier_len,
1601 StatementIndex stindex,
1602 boost::shared_ptr<PgSqlHostExchange> exchange) const;
1603
1613 void checkReadOnly(PgSqlHostContextPtr& ctx) const;
1614
1624 std::pair<uint32_t, uint32_t> getVersion(const std::string& timer_name = std::string()) const;
1625
1628
1632
1635
1639
1641 std::string timer_name_;
1642};
1643
1644namespace {
1645
1647typedef boost::array<PgSqlTaggedStatement, PgSqlHostDataSourceImpl::NUM_STATEMENTS>
1648TaggedStatementArray;
1649
1652TaggedStatementArray tagged_statements = { {
1653 // PgSqlHostDataSourceImpl::GET_HOST_DHCPID
1654 // Retrieves host information, IPv6 reservations and both DHCPv4 and
1655 // DHCPv6 options associated with the host. The LEFT JOIN clause is used
1656 // to retrieve information from 4 different tables using a single query.
1657 // Hence, this query returns multiple rows for a single host.
1658 {2,
1659 { OID_BYTEA, OID_INT2 },
1660 "get_host_dhcpid",
1661 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1662 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
1663 " h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
1664 " h.user_context, "
1665 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1666 " h.dhcp4_boot_file_name, h.auth_key, "
1667 " o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
1668 " o4.persistent, o4.cancelled, o4.user_context, "
1669 " o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
1670 " o6.persistent, o6.cancelled, o6.user_context, "
1671 " r.reservation_id, host(r.address), r.prefix_len, r.type, r.dhcp6_iaid "
1672 "FROM hosts AS h "
1673 "LEFT JOIN dhcp4_options AS o4 ON h.host_id = o4.host_id "
1674 "LEFT JOIN dhcp6_options AS o6 ON h.host_id = o6.host_id "
1675 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1676 "WHERE dhcp_identifier = $1 AND dhcp_identifier_type = $2 "
1677 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"
1678 },
1679
1680 // PgSqlHostDataSourceImpl::GET_HOST_ADDR
1681 // Retrieves host information along with the DHCPv4 options associated with
1682 // it. Left joining the dhcp4_options table results in multiple rows being
1683 // returned for the same host. The host is retrieved by IPv4 address.
1684 {1,
1685 { OID_INT8 },
1686 "get_host_addr",
1687 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1688 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1689 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1690 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1691 " h.dhcp4_boot_file_name, h.auth_key, "
1692 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1693 " o.persistent, o.cancelled, o.user_context "
1694 "FROM hosts AS h "
1695 "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1696 "WHERE ipv4_address = $1 "
1697 "ORDER BY h.host_id, o.option_id"
1698 },
1699
1700 // PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID
1701 // Retrieves host information and DHCPv4 options using subnet identifier
1702 // and client's identifier. Left joining the dhcp4_options table results in
1703 // multiple rows being returned for the same host.
1704 {3,
1706 "get_host_subid4_dhcpid",
1707 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1708 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1709 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1710 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1711 " h.dhcp4_boot_file_name, h.auth_key, "
1712 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1713 " o.persistent, o.cancelled, o.user_context "
1714 "FROM hosts AS h "
1715 "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1716 "WHERE h.dhcp4_subnet_id = $1 AND h.dhcp_identifier_type = $2 "
1717 " AND h.dhcp_identifier = $3 "
1718 "ORDER BY h.host_id, o.option_id"
1719 },
1720
1721 // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID
1722 // Retrieves host information, IPv6 reservations and DHCPv6 options
1723 // associated with a host. The number of rows returned is a multiplication
1724 // of number of IPv6 reservations and DHCPv6 options.
1725 {3,
1727 "get_host_subid6_dhcpid",
1728 "SELECT h.host_id, h.dhcp_identifier, "
1729 " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1730 " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1731 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1732 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1733 " h.dhcp4_boot_file_name, h.auth_key, "
1734 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1735 " o.persistent, o.cancelled, o.user_context, "
1736 " r.reservation_id, host(r.address), r.prefix_len, r.type, r.dhcp6_iaid "
1737 "FROM hosts AS h "
1738 "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1739 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1740 "WHERE h.dhcp6_subnet_id = $1 AND h.dhcp_identifier_type = $2 "
1741 " AND h.dhcp_identifier = $3 "
1742 "ORDER BY h.host_id, o.option_id, r.reservation_id"
1743 },
1744
1745 // PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR
1746 // Retrieves host information and DHCPv4 options for the host using subnet
1747 // identifier and IPv4 reservation. Left joining the dhcp4_options table
1748 // results in multiple rows being returned for the host. The number of
1749 // rows depends on the number of options defined for the host.
1750 {2,
1751 { OID_INT8, OID_INT8 },
1752 "get_host_subid_addr",
1753 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1754 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1755 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1756 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1757 " h.dhcp4_boot_file_name, h.auth_key, "
1758 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1759 " o.persistent, o.cancelled, o.user_context "
1760 "FROM hosts AS h "
1761 "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1762 "WHERE h.dhcp4_subnet_id = $1 AND h.ipv4_address = $2 "
1763 "ORDER BY h.host_id, o.option_id"
1764 },
1765
1766 // PgSqlHostDataSourceImpl::GET_HOST_PREFIX
1767 // Retrieves host information, IPv6 reservations and DHCPv6 options
1768 // associated with a host using prefix and prefix length. This query
1769 // returns host information for a single host. However, multiple rows
1770 // are returned due to left joining IPv6 reservations and DHCPv6 options.
1771 // The number of rows returned is multiplication of number of existing
1772 // IPv6 reservations and DHCPv6 options.
1773 {2,
1774 { OID_VARCHAR, OID_INT2 },
1775 "get_host_prefix",
1776 "SELECT h.host_id, h.dhcp_identifier, "
1777 " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1778 " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1779 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1780 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1781 " h.dhcp4_boot_file_name, h.auth_key, "
1782 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1783 " o.persistent, o.cancelled, o.user_context, "
1784 " r.reservation_id, host(r.address), r.prefix_len, r.type, "
1785 " r.dhcp6_iaid "
1786 "FROM hosts AS h "
1787 "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1788 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1789 "WHERE h.host_id = "
1790 " (SELECT host_id FROM ipv6_reservations "
1791 " WHERE address = cast($1 as inet) AND prefix_len = $2) "
1792 "ORDER BY h.host_id, o.option_id, r.reservation_id"
1793 },
1794
1795 // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR
1796 // Retrieves host information, IPv6 reservations and DHCPv6 options
1797 // associated with a host using IPv6 subnet id and prefix. This query
1798 // returns host information for a single host. However, multiple rows
1799 // are returned due to left joining IPv6 reservations and DHCPv6 options.
1800 // The number of rows returned is multiplication of number of existing
1801 // IPv6 reservations and DHCPv6 options.
1802 {2,
1803 { OID_INT8, OID_VARCHAR },
1804 "get_host_subid6_addr",
1805 "SELECT h.host_id, h.dhcp_identifier, "
1806 " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1807 " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1808 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1809 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1810 " h.dhcp4_boot_file_name, h.auth_key, "
1811 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1812 " o.persistent, o.cancelled, o.user_context, "
1813 " r.reservation_id, host(r.address), r.prefix_len, r.type, "
1814 " r.dhcp6_iaid "
1815 "FROM hosts AS h "
1816 "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1817 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1818 "WHERE h.dhcp6_subnet_id = $1 AND h.host_id IN "
1819 " (SELECT host_id FROM ipv6_reservations "
1820 " WHERE address = cast($2 as inet)) "
1821 "ORDER BY h.host_id, o.option_id, r.reservation_id"
1822 },
1823
1824 // PgSqlHostDataSourceImpl::GET_HOST_ADDR6
1825 // Retrieves host information, IPv6 reservations and DHCPv6 options
1826 // associated with a host using IPv6 address/prefix. This query
1827 // may return host information for one or more host reservations. Even
1828 // if only one host is found, multiple rows
1829 // are returned due to left joining IPv6 reservations and DHCPv6 options.
1830 // The number of rows returned is multiplication of number of existing
1831 // IPv6 reservations and DHCPv6 options.
1832 {1,
1833 { OID_VARCHAR },
1834 "get_host_addr6",
1835 "SELECT h.host_id, h.dhcp_identifier, "
1836 " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1837 " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1838 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1839 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1840 " h.dhcp4_boot_file_name, h.auth_key, "
1841 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1842 " o.persistent, o.cancelled, o.user_context, "
1843 " r.reservation_id, r.address, r.prefix_len, r.type, "
1844 " r.dhcp6_iaid "
1845 "FROM hosts AS h "
1846 "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1847 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1848 "WHERE h.host_id IN "
1849 " (SELECT host_id FROM ipv6_reservations "
1850 " WHERE address = cast($1 as inet)) "
1851 "ORDER BY h.host_id, o.option_id, r.reservation_id"
1852 },
1853
1854 // PgSqlHostDataSourceImpl::GET_HOST_SUBID4
1855 //
1856 // Retrieves host information for all hosts in a subnet, along with the
1857 // DHCPv4 options associated with it. Left joining the dhcp4_options table
1858 // results in multiple rows being returned for the same host. The hosts are
1859 // retrieved by subnet id.
1860 {1,
1861 { OID_INT8 },
1862 "get_host_subid4",
1863 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1864 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1865 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1866 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1867 " h.dhcp4_boot_file_name, h.auth_key, "
1868 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1869 " o.persistent, o.cancelled, o.user_context "
1870 "FROM hosts AS h "
1871 "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1872 "WHERE h.dhcp4_subnet_id = $1 "
1873 "ORDER BY h.host_id, o.option_id"
1874 },
1875
1876 // PgSqlHostDataSourceImpl::GET_HOST_SUBID6
1877 //
1878 // Retrieves host information, IPv6 reservations and DHCPv6 options
1879 // associated with all hosts using the IPv6 subnet id. This query returns
1880 // host information for many hosts. However, multiple rows are
1881 // returned due to left joining IPv6 reservations and DHCPv6 options.
1882 // The number of rows returned is multiplication of number of existing
1883 // IPv6 reservations and DHCPv6 options for each host in a subnet. There
1884 // are usually many hosts in a subnet. The amount of returned data may
1885 // be huge.
1886 {1,
1887 { OID_INT8 },
1888 "get_host_subid6",
1889 "SELECT h.host_id, h.dhcp_identifier, "
1890 " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1891 " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1892 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1893 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1894 " h.dhcp4_boot_file_name, h.auth_key, "
1895 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1896 " o.persistent, o.cancelled, o.user_context, "
1897 " r.reservation_id, host(r.address), r.prefix_len, r.type, r.dhcp6_iaid "
1898 "FROM hosts AS h "
1899 "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1900 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1901 "WHERE h.dhcp6_subnet_id = $1 "
1902 "ORDER BY h.host_id, o.option_id, r.reservation_id"
1903 },
1904
1905 // PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME
1906 // Retrieves host information, IPv6 reservations and both DHCPv4 and
1907 // DHCPv6 options associated with all hosts using the hostname.
1908 // The LEFT JOIN clause is used to retrieve information from 4 different
1909 // tables using a single query. Hence, this query returns multiple rows
1910 // for a single host.
1911 {1,
1912 { OID_VARCHAR },
1913 "get_host_hostname",
1914 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1915 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
1916 " h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
1917 " h.user_context, "
1918 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1919 " h.dhcp4_boot_file_name, h.auth_key, "
1920 " o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
1921 " o4.persistent, o4.cancelled, o4.user_context, "
1922 " o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
1923 " o6.persistent, o6.cancelled, o6.user_context, "
1924 " r.reservation_id, host(r.address), r.prefix_len, r.type, r.dhcp6_iaid "
1925 "FROM hosts AS h "
1926 "LEFT JOIN dhcp4_options AS o4 ON h.host_id = o4.host_id "
1927 "LEFT JOIN dhcp6_options AS o6 ON h.host_id = o6.host_id "
1928 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1929 "WHERE lower(h.hostname) = $1 "
1930 "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"
1931 },
1932
1933 // PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4
1934 // Retrieves host information for all hosts with a hostname in a subnet,
1935 // along with the DHCPv4 options associated with it. Left joining
1936 // the dhcp4_options table results in multiple rows being returned for
1937 // the same host.
1938 {2,
1939 { OID_VARCHAR, OID_INT8 },
1940 "get_host_hostname_subid4",
1941 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1942 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1943 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1944 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1945 " h.dhcp4_boot_file_name, h.auth_key, "
1946 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1947 " o.persistent, o.cancelled, o.user_context "
1948 "FROM hosts AS h "
1949 "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1950 "WHERE lower(h.hostname) = $1 AND h.dhcp4_subnet_id = $2 "
1951 "ORDER BY h.host_id, o.option_id"
1952 },
1953
1954 // PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6
1955 // Retrieves host information, IPv6 reservations and DHCPv6 options
1956 // associated with all hosts using the hostname and the IPv6 subnet id.
1957 // This query returns host information for many hosts. However, multiple
1958 // rows are returned due to left joining IPv6 reservations and DHCPv6
1959 // options. The number of rows returned is multiplication of number of
1960 // existing IPv6 reservations and DHCPv6 options for each host in a subnet.
1961 {2,
1962 { OID_VARCHAR, OID_INT8 },
1963 "get_host_hostname_subid6",
1964 "SELECT h.host_id, h.dhcp_identifier, "
1965 " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1966 " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1967 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1968 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1969 " h.dhcp4_boot_file_name, h.auth_key, "
1970 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1971 " o.persistent, o.cancelled, o.user_context, "
1972 " r.reservation_id, host(r.address), r.prefix_len, r.type, r.dhcp6_iaid "
1973 "FROM hosts AS h "
1974 "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1975 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1976 "WHERE lower(h.hostname) = $1 AND h.dhcp6_subnet_id = $2 "
1977 "ORDER BY h.host_id, o.option_id, r.reservation_id"
1978 },
1979
1980 // PgSqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE
1981 // Retrieves host information along with the DHCPv4 options associated with
1982 // it. Left joining the dhcp4_options table results in multiple rows being
1983 // returned for the same host. The hosts are retrieved by subnet id,
1984 // starting from specified host id. Specified number of hosts is returned.
1985 {3,
1987 "get_host_subid4_page",
1988 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1989 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1990 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1991 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1992 " h.dhcp4_boot_file_name, h.auth_key, "
1993 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1994 " o.persistent, o.cancelled, o.user_context "
1995 "FROM ( SELECT * FROM hosts AS h "
1996 " WHERE h.dhcp4_subnet_id = $1 AND h.host_id > $2 "
1997 " ORDER BY h.host_id "
1998 " LIMIT $3 ) AS h "
1999 "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
2000 "ORDER BY h.host_id, o.option_id"
2001 },
2002
2003 // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE
2004 // Retrieves host information, IPv6 reservations and DHCPv6 options
2005 // associated with a host using IPv6 subnet id. This query returns
2006 // host information for a single host. However, multiple rows are
2007 // returned due to left joining IPv6 reservations and DHCPv6 options.
2008 // The number of rows returned is multiplication of number of existing
2009 // IPv6 reservations and DHCPv6 options.
2010 {3,
2012 "get_host_subid6_page",
2013 "SELECT h.host_id, h.dhcp_identifier, "
2014 " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2015 " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2016 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2017 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
2018 " h.dhcp4_boot_file_name, h.auth_key, "
2019 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
2020 " o.persistent, o.cancelled, o.user_context, "
2021 " r.reservation_id, host(r.address), r.prefix_len, r.type, r.dhcp6_iaid "
2022 "FROM ( SELECT * FROM hosts AS h "
2023 " WHERE h.dhcp6_subnet_id = $1 AND h.host_id > $2 "
2024 " ORDER BY h.host_id "
2025 " LIMIT $3 ) AS h "
2026 "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
2027 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
2028 "ORDER BY h.host_id, o.option_id, r.reservation_id"
2029 },
2030
2031 // PgSqlHostDataSourceImpl::GET_HOST_PAGE4
2032 // Retrieves host information along with the DHCPv4 options associated with
2033 // it. Left joining the dhcp4_options table results in multiple rows being
2034 // returned for the same host. The hosts are retrieved starting from
2035 // specified host id. Specified number of hosts is returned.
2036 {2,
2037 { OID_INT8, OID_INT8 },
2038 "get_host_page4",
2039 "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
2040 " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2041 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2042 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
2043 " h.dhcp4_boot_file_name, h.auth_key, "
2044 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
2045 " o.persistent, o.cancelled, o.user_context "
2046 "FROM ( SELECT * FROM hosts AS h "
2047 " WHERE h.host_id > $1 "
2048 " ORDER BY h.host_id "
2049 " LIMIT $2 ) AS h "
2050 "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
2051 "ORDER BY h.host_id, o.option_id"
2052 },
2053
2054 // PgSqlHostDataSourceImpl::GET_HOST_PAGE6
2055 // Retrieves host information, IPv6 reservations and DHCPv6 options
2056 // associated with a host using IPv6 subnet id. This query returns
2057 // host information for a single host. However, multiple rows are
2058 // returned due to left joining IPv6 reservations and DHCPv6 options.
2059 // The number of rows returned is multiplication of number of existing
2060 // IPv6 reservations and DHCPv6 options.
2061 {2,
2062 { OID_INT8, OID_INT8 },
2063 "get_host_page6",
2064 "SELECT h.host_id, h.dhcp_identifier, "
2065 " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2066 " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2067 " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2068 " h.dhcp4_next_server, h.dhcp4_server_hostname, "
2069 " h.dhcp4_boot_file_name, h.auth_key, "
2070 " o.option_id, o.code, o.value, o.formatted_value, o.space, "
2071 " o.persistent, o.cancelled, o.user_context, "
2072 " r.reservation_id, host(r.address), r.prefix_len, r.type, r.dhcp6_iaid "
2073 "FROM ( SELECT * FROM hosts AS h "
2074 " WHERE h.host_id > $1 "
2075 " ORDER BY h.host_id "
2076 " LIMIT $2 ) AS h "
2077 "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
2078 "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
2079 "ORDER BY h.host_id, o.option_id, r.reservation_id"
2080 },
2081
2082 // PgSqlHostDataSourceImpl::INSERT_HOST_NON_UNIQUE_IP
2083 // Inserts a host into the 'hosts' table without checking that there is
2084 // a reservation for the IP address.
2085 {13,
2090 "insert_host_non_unique_ip",
2091 "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
2092 " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2093 " dhcp4_client_classes, dhcp6_client_classes, user_context, "
2094 " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key)"
2095 "VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 ) "
2096 "RETURNING host_id"
2097 },
2098
2099 // PgSqlHostDataSourceImpl::INSERT_HOST_UNIQUE_IP
2100 // Inserts a host into the 'hosts' table with checking that reserved IP
2101 // address is unique. The innermost query checks if there is at least
2102 // one host for the given IP/subnet combination. For checking whether
2103 // hosts exists or not it doesn't matter if we select actual columns,
2104 // thus SELECT 1 was used as an optimization to avoid selecting data
2105 // that will be ignored anyway. If it does not exist the new host is
2106 // inserted. If the host with the given IP address already exists the
2107 // new host won't be inserted. The caller can check the number of
2108 // affected rows to detect that there was a duplicate host in the
2109 // database. Returns the inserted host id.
2110 {15,
2116 "insert_host_unique_ip",
2117 "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
2118 " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2119 " dhcp4_client_classes, dhcp6_client_classes, user_context, "
2120 " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key)"
2121 " SELECT $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13"
2122 " WHERE NOT EXISTS ("
2123 " SELECT 1 FROM hosts WHERE ipv4_address = $14 AND dhcp4_subnet_id = $15"
2124 " LIMIT 1"
2125 " ) "
2126 "RETURNING host_id"
2127 },
2128
2129 // PgSqlHostDataSourceImpl::INSERT_V6_RESRV_NON_UNIQUE
2130 // Inserts a single IPv6 reservation into 'reservations' table without
2131 // checking that the inserted reservation is unique.
2132 {5,
2134 "insert_v6_resrv_non_unique",
2135 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2136 " dhcp6_iaid, host_id) "
2137 "VALUES (cast($1 as inet), $2, $3, $4, $5)"
2138 },
2139
2140 // PgSqlHostDataSourceImpl::INSERT_V6_RESRV_UNIQUE
2141 // Inserts a single IPv6 reservation into 'reservations' table with
2142 // checking that the inserted reservation is unique.
2143 {7,
2145 "insert_v6_resrv_unique",
2146 "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2147 " dhcp6_iaid, host_id) "
2148 "SELECT cast($1 as inet), $2, $3, $4, $5 "
2149 " WHERE NOT EXISTS ("
2150 " SELECT 1 FROM ipv6_reservations"
2151 " WHERE address = cast($6 as inet) AND prefix_len = $7"
2152 " LIMIT 1"
2153 " )"
2154 },
2155
2156 // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
2157 // Inserts a single DHCPv4 option into 'dhcp4_options' table.
2158 // Using fixed scope_id = 3, which associates an option with host.
2159 {8,
2162 "insert_v4_host_option",
2163 "INSERT INTO dhcp4_options(code, value, formatted_value, space, "
2164 " persistent, cancelled, user_context, host_id, scope_id) "
2165 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 3)"
2166 },
2167
2168 // PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
2169 // Inserts a single DHCPv6 option into 'dhcp6_options' table.
2170 // Using fixed scope_id = 3, which associates an option with host.
2171 {8,
2174 "insert_v6_host_option",
2175 "INSERT INTO dhcp6_options(code, value, formatted_value, space, "
2176 " persistent, cancelled, user_context, host_id, scope_id) "
2177 "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 3)"
2178 },
2179
2180 // PgSqlHostDataSourceImpl::DEL_HOST_ADDR4
2181 // Deletes a v4 host that matches (subnet-id, addr4)
2182 {2,
2183 { OID_INT8, OID_INT8 },
2184 "del_host_addr4",
2185 "DELETE FROM hosts WHERE dhcp4_subnet_id = $1 AND ipv4_address = $2"
2186 },
2187
2188 // PgSqlHostDataSourceImpl::DEL_HOST_ADDR6
2189 // Deletes a v6 host that matches (subnet-id, addr6)
2190 {2,
2191 { OID_INT8, OID_VARCHAR },
2192 "del_host_addr6",
2193 "DELETE FROM hosts USING ipv6_reservations "
2194 " WHERE dhcp6_subnet_id = $1 AND ipv6_reservations.address = cast($2 as inet)"
2195 },
2196
2197 // PgSqlHostDataSourceImpl::DEL_HOST_SUBID4_ID
2198 // Deletes a v4 host that matches (subnet4-id, identifier-type, identifier)
2199 {3,
2201 "del_host_subid4_id",
2202 "DELETE FROM hosts WHERE dhcp4_subnet_id = $1 "
2203 "AND dhcp_identifier_type = $2 "
2204 "AND dhcp_identifier = $3"
2205 },
2206
2207 // PgSqlHostDataSourceImpl::DEL_HOST_SUBID6_ID
2208 // Deletes a v6 host that matches (subnet6-id, identifier-type, identifier)
2209 {3,
2211 "del_host_subid6_id",
2212 "DELETE FROM hosts WHERE dhcp6_subnet_id = $1 "
2213 "AND dhcp_identifier_type = $2 "
2214 "AND dhcp_identifier = $3"
2215 }
2216}
2217};
2218
2219} // namespace
2220
2221// PgSqlHostContext Constructor
2222
2224 IOServiceAccessorPtr io_service_accessor,
2225 db::DbCallback db_reconnect_callback)
2226 : conn_(parameters, io_service_accessor, db_reconnect_callback),
2227 is_readonly_(true) {
2228}
2229
2230// PgSqlHostContextAlloc Constructor and Destructor
2231
2233 PgSqlHostDataSourceImpl& mgr) : ctx_(), mgr_(mgr) {
2234
2235 if (MultiThreadingMgr::instance().getMode()) {
2236 // multi-threaded
2237 {
2238 // we need to protect the whole pool_ operation, hence extra scope {}
2239 lock_guard<mutex> lock(mgr_.pool_->mutex_);
2240 if (!mgr_.pool_->pool_.empty()) {
2241 ctx_ = mgr_.pool_->pool_.back();
2242 mgr_.pool_->pool_.pop_back();
2243 }
2244 }
2245 if (!ctx_) {
2246 ctx_ = mgr_.createContext();
2247 }
2248 } else {
2249 // single-threaded
2250 if (mgr_.pool_->pool_.empty()) {
2251 isc_throw(Unexpected, "No available PostgreSQL host context?!");
2252 }
2253 ctx_ = mgr_.pool_->pool_.back();
2254 }
2255}
2256
2258 if (MultiThreadingMgr::instance().getMode()) {
2259 // multi-threaded
2260 lock_guard<mutex> lock(mgr_.pool_->mutex_);
2261 mgr_.pool_->pool_.push_back(ctx_);
2262 if (ctx_->conn_.isUnusable()) {
2263 mgr_.unusable_ = true;
2264 }
2265 } else if (ctx_->conn_.isUnusable()) {
2266 mgr_.unusable_ = true;
2267 }
2268}
2269
2271 : parameters_(parameters), ip_reservations_unique_(true), unusable_(false) {
2272
2273 // Check TLS support.
2274 size_t tls(0);
2275 tls += parameters.count("trust-anchor");
2276 tls += parameters.count("cert-file");
2277 tls += parameters.count("key-file");
2278 tls += parameters.count("cipher-list");
2279#ifdef HAVE_PGSQL_SSL
2280 if ((tls > 0) && !PgSqlConnection::warned_about_tls) {
2284 PQinitSSL(1);
2285 }
2286#else
2287 if (tls > 0) {
2290 isc_throw(DbOpenError, "Attempt to configure TLS for PostgreSQL "
2291 << "backend (built with this feature disabled)");
2292 }
2293#endif
2294
2295 // Create unique timer name per instance.
2296 timer_name_ = "PgSqlHostMgr[";
2297 timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
2298 timer_name_ += "]DbReconnectTimer";
2299
2302 timer_name_);
2303
2304 // Create an initial context.
2305 pool_.reset(new PgSqlHostContextPool());
2306 pool_->pool_.push_back(createContext());
2307}
2308
2309// Create context.
2310
2316
2317 // Open the database.
2318 ctx->conn_.openDatabase();
2319
2320 // Now prepare the SQL statements.
2321 ctx->conn_.prepareStatements(tagged_statements.begin(),
2322 tagged_statements.begin() + WRITE_STMTS_BEGIN);
2323
2324 // Check if the backend is explicitly configured to operate with
2325 // read only access to the database.
2326 ctx->is_readonly_ = ctx->conn_.configuredReadOnly();
2327
2328 // If we are using read-write mode for the database we also prepare
2329 // statements for INSERTS etc.
2330 if (!ctx->is_readonly_) {
2331 ctx->conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
2332 tagged_statements.end());
2333 } else {
2335 }
2336
2337 ctx->host_ipv4_exchange_.reset(new PgSqlHostWithOptionsExchange(PgSqlHostWithOptionsExchange::DHCP4_ONLY));
2338 ctx->host_ipv6_exchange_.reset(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP6_ONLY));
2339 ctx->host_ipv46_exchange_.reset(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP4_AND_DHCP6));
2340 ctx->host_ipv6_reservation_exchange_.reset(new PgSqlIPv6ReservationExchange());
2341 ctx->host_option_exchange_.reset(new PgSqlOptionExchange());
2342
2343 // Create ReconnectCtl for this connection.
2344 ctx->conn_.makeReconnectCtl(timer_name_);
2345
2346 return (ctx);
2347}
2348
2350}
2351
2352bool
2355
2356 // Invoke application layer connection lost callback.
2357 if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
2358 return (false);
2359 }
2360
2361 bool reopened = false;
2362
2363 const std::string timer_name = db_reconnect_ctl->timerName();
2364
2365 // At least one connection was lost.
2366 try {
2367 CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
2368 std::list<std::string> host_db_access_list = cfg_db->getHostDbAccessStringList();
2369 for (std::string& hds : host_db_access_list) {
2370 auto parameters = DatabaseConnection::parse(hds);
2371 if (HostMgr::delBackend("postgresql", hds, true)) {
2373 }
2374 }
2375 reopened = true;
2376 } catch (const std::exception& ex) {
2378 .arg(ex.what());
2379 }
2380
2381 if (reopened) {
2382 // Cancel the timer.
2383 if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2384 TimerMgr::instance()->unregisterTimer(timer_name);
2385 }
2386
2387 // Invoke application layer connection recovered callback.
2388 if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
2389 return (false);
2390 }
2391 } else {
2392 if (!db_reconnect_ctl->checkRetries()) {
2393 // We're out of retries, log it and initiate shutdown.
2395 .arg(db_reconnect_ctl->maxRetries());
2396
2397 // Cancel the timer.
2398 if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2399 TimerMgr::instance()->unregisterTimer(timer_name);
2400 }
2401
2402 // Invoke application layer connection failed callback.
2404 return (false);
2405 }
2406
2408 .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
2409 .arg(db_reconnect_ctl->maxRetries())
2410 .arg(db_reconnect_ctl->retryInterval());
2411
2412 // Start the timer.
2413 if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
2414 TimerMgr::instance()->registerTimer(timer_name,
2415 std::bind(&PgSqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl),
2416 db_reconnect_ctl->retryInterval(),
2418 }
2419 TimerMgr::instance()->setup(timer_name);
2420 }
2421
2422 return (true);
2423}
2424
2425uint64_t
2427 StatementIndex stindex,
2428 PsqlBindArrayPtr& bind_array,
2429 const bool return_last_id) {
2430 uint64_t last_id = 0;
2431 PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
2432 tagged_statements[stindex].nbparams,
2433 &bind_array->values_[0],
2434 &bind_array->lengths_[0],
2435 &bind_array->formats_[0], 0));
2436
2437 int s = PQresultStatus(r);
2438
2439 if (s != PGRES_COMMAND_OK) {
2440 // Failure: check for the special case of duplicate entry.
2441 if (ctx->conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
2442 isc_throw(DuplicateEntry, "Database duplicate entry error");
2443 }
2444
2445 // Connection determines if the error is fatal or not, and
2446 // throws the appropriate exception
2447 ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
2448 }
2449
2450 // Get the number of affected rows.
2451 char* rows_affected = PQcmdTuples(r);
2452 if (!rows_affected) {
2454 "Could not retrieve the number of affected rows.");
2455 }
2456
2457 // If the number of rows inserted is 0 it means that the query detected
2458 // an attempt to insert duplicated data for which there is no unique
2459 // index in the database. Unique indexes are not created in the database
2460 // when it may be sometimes allowed to insert duplicated records per
2461 // server's configuration.
2462 if (rows_affected[0] == '0') {
2463 isc_throw(DuplicateEntry, "Database duplicate entry error");
2464 }
2465
2466 if (return_last_id) {
2467 PgSqlExchange::getColumnValue(r, 0, 0, last_id);
2468 }
2469
2470 return (last_id);
2471}
2472
2473bool
2475 StatementIndex stindex,
2476 PsqlBindArrayPtr& bind_array) {
2477 PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
2478 tagged_statements[stindex].nbparams,
2479 &bind_array->values_[0],
2480 &bind_array->lengths_[0],
2481 &bind_array->formats_[0], 0));
2482
2483 int s = PQresultStatus(r);
2484
2485 if (s != PGRES_COMMAND_OK) {
2486 // Connection determines if the error is fatal or not, and
2487 // throws the appropriate exception
2488 ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
2489 }
2490
2491 // Now check how many rows (hosts) were deleted. This should be either
2492 // "0" or "1".
2493 char* rows_deleted = PQcmdTuples(r);
2494 if (!rows_deleted) {
2496 "Could not retrieve the number of deleted rows.");
2497 }
2498 return (rows_deleted[0] != '0');
2499}
2500
2501void
2503 const IPv6Resrv& resv,
2504 const HostID& id) {
2505 PsqlBindArrayPtr bind_array = ctx->host_ipv6_reservation_exchange_->
2506 createBindForSend(resv, id, ip_reservations_unique_);
2507
2508 addStatement(ctx,
2510 bind_array);
2511}
2512
2513void
2515 const StatementIndex& stindex,
2516 const OptionDescriptor& opt_desc,
2517 const std::string& opt_space,
2518 const Optional<SubnetID>&,
2519 const HostID& id) {
2520 PsqlBindArrayPtr bind_array = ctx->host_option_exchange_->createBindForSend(opt_desc, opt_space, id);
2521
2522 addStatement(ctx, stindex, bind_array);
2523}
2524
2525void
2527 const StatementIndex& stindex,
2528 const ConstCfgOptionPtr& options_cfg,
2529 const uint64_t host_id) {
2530 // Get option space names and vendor space names and combine them within a
2531 // single list.
2532 std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
2533 std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
2534 option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
2535 vendor_spaces.end());
2536
2537 // For each option space retrieve all options and insert them into the
2538 // database.
2539 for (auto const& space : option_spaces) {
2540 OptionContainerPtr options = options_cfg->getAllCombined(space);
2541 if (options && !options->empty()) {
2542 for (auto const& opt : *options) {
2543 addOption(ctx, stindex, opt, space, Optional<SubnetID>(), host_id);
2544 }
2545 }
2546 }
2547}
2548
2549void
2551 StatementIndex stindex,
2552 PsqlBindArrayPtr bind_array,
2553 boost::shared_ptr<PgSqlHostExchange> exchange,
2554 ConstHostCollection& result,
2555 bool single) const {
2556
2557 exchange->clear();
2558 PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
2559 tagged_statements[stindex].nbparams,
2560 &bind_array->values_[0],
2561 &bind_array->lengths_[0],
2562 &bind_array->formats_[0], 0));
2563
2564 ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
2565
2566 int rows = r.getRows();
2567 for (int row = 0; row < rows; ++row) {
2568 exchange->processRowData(result, r, row);
2569
2570 if (single && result.size() > 1) {
2571 isc_throw(MultipleRecords, "multiple records were found in the "
2572 "database where only one was expected for query "
2573 << tagged_statements[stindex].name);
2574 }
2575 }
2576}
2577
2580 const SubnetID& subnet_id,
2581 const Host::IdentifierType& identifier_type,
2582 const uint8_t* identifier_begin,
2583 const size_t identifier_len,
2584 StatementIndex stindex,
2585 boost::shared_ptr<PgSqlHostExchange> exchange) const {
2586
2587 // Set up the WHERE clause value
2588 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2589
2590 // Add the subnet id.
2591 bind_array->add(subnet_id);
2592
2593 // Add the Identifier type.
2594 bind_array->add(static_cast<uint8_t>(identifier_type));
2595
2596 // Add the identifier value.
2597 bind_array->add(identifier_begin, identifier_len);
2598
2599 ConstHostCollection collection;
2600 getHostCollection(ctx, stindex, bind_array, exchange, collection, true);
2601
2602 // Return single record if present, else clear the host.
2603 ConstHostPtr result;
2604 if (!collection.empty()) {
2605 result = *collection.begin();
2606 }
2607
2608 return (result);
2609}
2610
2611std::pair<uint32_t, uint32_t>
2612PgSqlHostDataSourceImpl::getVersion(const std::string& timer_name) const {
2614
2617
2618 return (PgSqlConnection::getVersion(parameters_, ac, cb, timer_name));
2619}
2620
2621void
2623 if (ctx->is_readonly_) {
2624 isc_throw(ReadOnlyDb, "PostgreSQL host database backend is configured"
2625 " to operate in read only mode");
2626 }
2627}
2628
2629/*********** PgSqlHostDataSource *********************/
2630
2632 : impl_(new PgSqlHostDataSourceImpl(parameters)) {
2633}
2634
2636}
2637
2640 return (impl_->parameters_);
2641}
2642
2643void
2645 // Get a context
2646 PgSqlHostContextAlloc get_context(*impl_);
2647 PgSqlHostContextPtr ctx = get_context.ctx_;
2648
2649 // If operating in read-only mode, throw exception.
2650 impl_->checkReadOnly(ctx);
2651
2652 // Initiate PostgreSQL transaction as we will have to make multiple queries
2653 // to insert host information into multiple tables. If that fails on
2654 // any stage, the transaction will be rolled back by the destructor of
2655 // the PgSqlTransaction class.
2656 PgSqlTransaction transaction(ctx->conn_);
2657
2658 // If we're configured to check that an IP reservation within a given subnet
2659 // is unique, the IP reservation exists and the subnet is actually set
2660 // we will be using a special query that checks for uniqueness. Otherwise,
2661 // we will use a regular insert statement.
2662 bool unique_ip = impl_->ip_reservations_unique_ && !host->getIPv4Reservation().isV4Zero()
2663 && host->getIPv4SubnetID() != SUBNET_ID_UNUSED;
2664
2665 // Create the PgSQL Bind array for the host
2666 PsqlBindArrayPtr bind_array = ctx->host_ipv4_exchange_->createBindForSend(host, unique_ip);
2667
2668 // ... and insert the host.
2669 uint32_t host_id = impl_->addStatement(ctx,
2672 bind_array, true);
2673
2674 // Insert DHCPv4 options.
2675 ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
2676 if (cfg_option4) {
2678 cfg_option4, host_id);
2679 }
2680
2681 // Insert DHCPv6 options.
2682 ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
2683 if (cfg_option6) {
2685 cfg_option6, host_id);
2686 }
2687
2688 // Insert IPv6 reservations.
2689 IPv6ResrvRange v6resv = host->getIPv6Reservations();
2690 if (std::distance(v6resv.first, v6resv.second) > 0) {
2691 BOOST_FOREACH(auto const& resv, v6resv) {
2692 impl_->addResv(ctx, resv.second, host_id);
2693 }
2694 }
2695
2696 // Everything went fine, so explicitly commit the transaction.
2697 transaction.commit();
2698}
2699
2700bool
2702 const asiolink::IOAddress& addr) {
2703 // Get a context
2704 PgSqlHostContextAlloc get_context(*impl_);
2705 PgSqlHostContextPtr ctx = get_context.ctx_;
2706
2707 // If operating in read-only mode, throw exception.
2708 impl_->checkReadOnly(ctx);
2709
2710 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2711 bind_array->add(subnet_id);
2712
2713 // v4
2714 if (addr.isV4()) {
2715 bind_array->add(addr);
2716 return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_ADDR4,
2717 bind_array));
2718 }
2719
2720 // v6
2721 bind_array->addTempString(addr.toText());
2722
2723 return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_ADDR6,
2724 bind_array));
2725}
2726
2727bool
2729 const Host::IdentifierType& identifier_type,
2730 const uint8_t* identifier_begin,
2731 const size_t identifier_len) {
2732 // Get a context
2733 PgSqlHostContextAlloc get_context(*impl_);
2734 PgSqlHostContextPtr ctx = get_context.ctx_;
2735
2736 // If operating in read-only mode, throw exception.
2737 impl_->checkReadOnly(ctx);
2738
2739 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2740
2741 // Subnet-id
2742 bind_array->add(subnet_id);
2743
2744 // identifier-type
2745 bind_array->add(static_cast<uint8_t>(identifier_type));
2746
2747 // identifier
2748 bind_array->add(identifier_begin, identifier_len);
2749
2750 return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_SUBID4_ID,
2751 bind_array));
2752}
2753
2754bool
2756 const Host::IdentifierType& identifier_type,
2757 const uint8_t* identifier_begin,
2758 const size_t identifier_len) {
2759 // Get a context
2760 PgSqlHostContextAlloc get_context(*impl_);
2761 PgSqlHostContextPtr ctx = get_context.ctx_;
2762
2763 // If operating in read-only mode, throw exception.
2764 impl_->checkReadOnly(ctx);
2765
2766 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2767
2768 // Subnet-id
2769 bind_array->add(subnet_id);
2770
2771 // identifier-type
2772 bind_array->add(static_cast<uint8_t>(identifier_type));
2773
2774 // identifier
2775 bind_array->add(identifier_begin, identifier_len);
2776
2777 return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_SUBID6_ID,
2778 bind_array));
2779}
2780
2783 const uint8_t* identifier_begin,
2784 const size_t identifier_len) const {
2785 // Get a context
2786 PgSqlHostContextAlloc get_context(*impl_);
2787 PgSqlHostContextPtr ctx = get_context.ctx_;
2788
2789 // Set up the WHERE clause value
2790 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2791
2792 // Identifier value.
2793 bind_array->add(identifier_begin, identifier_len);
2794
2795 // Identifier type.
2796 bind_array->add(static_cast<uint8_t>(identifier_type));
2797
2798 ConstHostCollection result;
2799 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_DHCPID,
2800 bind_array, ctx->host_ipv46_exchange_, result, false);
2801
2802 return (result);
2803}
2804
2807 // Get a context
2808 PgSqlHostContextAlloc get_context(*impl_);
2809 PgSqlHostContextPtr ctx = get_context.ctx_;
2810
2811 // Set up the WHERE clause value
2812 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2813
2814 // Add the subnet id.
2815 bind_array->add(subnet_id);
2816
2817 ConstHostCollection result;
2818 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID4,
2819 bind_array, ctx->host_ipv4_exchange_, result, false);
2820
2821 return (result);
2822}
2823
2826 // Get a context
2827 PgSqlHostContextAlloc get_context(*impl_);
2828 PgSqlHostContextPtr ctx = get_context.ctx_;
2829
2830 // Set up the WHERE clause value
2831 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2832
2833 // Add the subnet id.
2834 bind_array->add(subnet_id);
2835
2836 ConstHostCollection result;
2837 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6,
2838 bind_array, ctx->host_ipv6_exchange_, result, false);
2839
2840 return (result);
2841}
2842
2844PgSqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
2845 // Get a context
2846 PgSqlHostContextAlloc get_context(*impl_);
2847 PgSqlHostContextPtr ctx = get_context.ctx_;
2848
2849 // Set up the WHERE clause value
2850 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2851
2852 // Add the hostname.
2853 bind_array->add(hostname);
2854
2855 ConstHostCollection result;
2856 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME,
2857 bind_array, ctx->host_ipv46_exchange_, result, false);
2858
2859 return (result);
2860}
2861
2863PgSqlHostDataSource::getAllbyHostname4(const std::string& hostname,
2864 const SubnetID& subnet_id) const {
2865 // Get a context
2866 PgSqlHostContextAlloc get_context(*impl_);
2867 PgSqlHostContextPtr ctx = get_context.ctx_;
2868
2869 // Set up the WHERE clause value
2870 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2871
2872 // Add the hostname.
2873 bind_array->add(hostname);
2874
2875 // Add the subnet id.
2876 bind_array->add(subnet_id);
2877
2878 ConstHostCollection result;
2879 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4,
2880 bind_array, ctx->host_ipv4_exchange_, result, false);
2881
2882 return (result);
2883}
2884
2886PgSqlHostDataSource::getAllbyHostname6(const std::string& hostname,
2887 const SubnetID& subnet_id) const {
2888 // Get a context
2889 PgSqlHostContextAlloc get_context(*impl_);
2890 PgSqlHostContextPtr ctx = get_context.ctx_;
2891
2892 // Set up the WHERE clause value
2893 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2894
2895 // Add the hostname.
2896 bind_array->add(hostname);
2897
2898 // Add the subnet id.
2899 bind_array->add(subnet_id);
2900
2901 ConstHostCollection result;
2902 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6,
2903 bind_array, ctx->host_ipv6_exchange_, result, false);
2904
2905 return (result);
2906}
2907
2910 size_t& /*source_index*/,
2911 uint64_t lower_host_id,
2912 const HostPageSize& page_size) const {
2913 // Get a context
2914 PgSqlHostContextAlloc get_context(*impl_);
2915 PgSqlHostContextPtr ctx = get_context.ctx_;
2916
2917 // Set up the WHERE clause value
2918 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2919
2920 // Add the subnet id.
2921 bind_array->add(subnet_id);
2922
2923 // Add the lower bound host id.
2924 bind_array->add(lower_host_id);
2925
2926 // Add the page size value.
2927 string page_size_data =
2928 boost::lexical_cast<std::string>(page_size.page_size_);
2929 bind_array->add(page_size_data);
2930
2931 ConstHostCollection result;
2932 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE,
2933 bind_array, ctx->host_ipv4_exchange_, result, false);
2934
2935 return (result);
2936}
2937
2940 size_t& /*source_index*/,
2941 uint64_t lower_host_id,
2942 const HostPageSize& page_size) const {
2943 // Get a context
2944 PgSqlHostContextAlloc get_context(*impl_);
2945 PgSqlHostContextPtr ctx = get_context.ctx_;
2946
2947 // Set up the WHERE clause value
2948 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2949
2950 // Add the subnet id.
2951 bind_array->add(subnet_id);
2952
2953 // Add the lower bound host id.
2954 bind_array->add(lower_host_id);
2955
2956 // Add the page size value.
2957 string page_size_data =
2958 boost::lexical_cast<std::string>(page_size.page_size_);
2959 bind_array->add(page_size_data);
2960
2961 ConstHostCollection result;
2962 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE,
2963 bind_array, ctx->host_ipv6_exchange_, result, false);
2964
2965 return (result);
2966}
2967
2969PgSqlHostDataSource::getPage4(size_t& /*source_index*/,
2970 uint64_t lower_host_id,
2971 const HostPageSize& page_size) const {
2972 // Get a context
2973 PgSqlHostContextAlloc get_context(*impl_);
2974 PgSqlHostContextPtr ctx = get_context.ctx_;
2975
2976 // Set up the WHERE clause value
2977 PsqlBindArrayPtr bind_array(new PsqlBindArray());
2978
2979 // Add the lower bound host id.
2980 bind_array->add(lower_host_id);
2981
2982 // Add the page size value.
2983 string page_size_data =
2984 boost::lexical_cast<std::string>(page_size.page_size_);
2985 bind_array->add(page_size_data);
2986
2987 ConstHostCollection result;
2988 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_PAGE4,
2989 bind_array, ctx->host_ipv4_exchange_, result, false);
2990
2991 return (result);
2992}
2993
2995PgSqlHostDataSource::getPage6(size_t& /*source_index*/,
2996 uint64_t lower_host_id,
2997 const HostPageSize& page_size) const {
2998 // Get a context
2999 PgSqlHostContextAlloc get_context(*impl_);
3000 PgSqlHostContextPtr ctx = get_context.ctx_;
3001
3002 // Set up the WHERE clause value
3003 PsqlBindArrayPtr bind_array(new PsqlBindArray());
3004
3005 // Add the lower bound host id.
3006 bind_array->add(lower_host_id);
3007
3008 // Add the page size value.
3009 string page_size_data =
3010 boost::lexical_cast<std::string>(page_size.page_size_);
3011 bind_array->add(page_size_data);
3012
3013 ConstHostCollection result;
3014 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_PAGE6,
3015 bind_array, ctx->host_ipv6_exchange_, result, false);
3016
3017 return (result);
3018}
3019
3022 // Get a context
3023 PgSqlHostContextAlloc get_context(*impl_);
3024 PgSqlHostContextPtr ctx = get_context.ctx_;
3025
3026 // Set up the WHERE clause value
3027 PsqlBindArrayPtr bind_array(new PsqlBindArray());
3028
3029 // v4 Reservation address
3030 bind_array->add(address);
3031
3032 ConstHostCollection result;
3033 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_ADDR,
3034 bind_array, ctx->host_ipv4_exchange_, result, false);
3035
3036 return (result);
3037}
3038
3041 const Host::IdentifierType& identifier_type,
3042 const uint8_t* identifier_begin,
3043 const size_t identifier_len) const {
3044 // Get a context
3045 PgSqlHostContextAlloc get_context(*impl_);
3046 PgSqlHostContextPtr ctx = get_context.ctx_;
3047
3048 return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3050 ctx->host_ipv4_exchange_));
3051}
3052
3055 const asiolink::IOAddress& address) const {
3056 // Get a context
3057 PgSqlHostContextAlloc get_context(*impl_);
3058 PgSqlHostContextPtr ctx = get_context.ctx_;
3059
3060 if (!address.isV4()) {
3061 isc_throw(BadValue, "PgSqlHostDataSource::get4(id, address) - "
3062 " wrong address type, address supplied is an IPv6 address");
3063 }
3064
3065 // Set up the WHERE clause value
3066 PsqlBindArrayPtr bind_array(new PsqlBindArray());
3067
3068 // Add the subnet id
3069 bind_array->add(subnet_id);
3070
3071 // Add the address
3072 bind_array->add(address);
3073
3074 ConstHostCollection collection;
3075 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
3076 bind_array, ctx->host_ipv4_exchange_, collection, true);
3077
3078 // Return single record if present, else clear the host.
3079 ConstHostPtr result;
3080 if (!collection.empty()) {
3081 result = *collection.begin();
3082 }
3083
3084 return (result);
3085}
3086
3089 const asiolink::IOAddress& address) const {
3090 // Get a context
3091 PgSqlHostContextAlloc get_context(*impl_);
3092 PgSqlHostContextPtr ctx = get_context.ctx_;
3093
3094 if (!address.isV4()) {
3095 isc_throw(BadValue, "PgSqlHostDataSource::get4(id, address) - "
3096 " wrong address type, address supplied is an IPv6 address");
3097 }
3098
3099 // Set up the WHERE clause value
3100 PsqlBindArrayPtr bind_array(new PsqlBindArray());
3101
3102 // Add the subnet id
3103 bind_array->add(subnet_id);
3104
3105 // Add the address
3106 bind_array->add(address);
3107
3108 ConstHostCollection collection;
3109 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
3110 bind_array, ctx->host_ipv4_exchange_, collection, false);
3111 return (collection);
3112}
3113
3116 const Host::IdentifierType& identifier_type,
3117 const uint8_t* identifier_begin,
3118 const size_t identifier_len) const {
3119 // Get a context
3120 PgSqlHostContextAlloc get_context(*impl_);
3121 PgSqlHostContextPtr ctx = get_context.ctx_;
3122
3123 return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3125 ctx->host_ipv6_exchange_));
3126}
3127
3130 const uint8_t prefix_len) const {
3131 if (!prefix.isV6()) {
3132 isc_throw(BadValue, "PgSqlHostDataSource::get6(prefix, prefix_len): "
3133 "wrong address type, address supplied is an IPv4 address");
3134 }
3135
3136 // Get a context
3137 PgSqlHostContextAlloc get_context(*impl_);
3138 PgSqlHostContextPtr ctx = get_context.ctx_;
3139
3140 // Set up the WHERE clause value
3141 PsqlBindArrayPtr bind_array(new PsqlBindArray());
3142
3143 // Add the prefix
3144 bind_array->add(prefix);
3145
3146 // Add the prefix length
3147 bind_array->add(prefix_len);
3148
3149 ConstHostCollection collection;
3150 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_PREFIX,
3151 bind_array, ctx->host_ipv6_exchange_, collection, true);
3152
3153 // Return single record if present, else clear the host.
3154 ConstHostPtr result;
3155 if (!collection.empty()) {
3156 result = *collection.begin();
3157 }
3158
3159 return (result);
3160}
3161
3164 const asiolink::IOAddress& address) const {
3165 if (!address.isV6()) {
3166 isc_throw(BadValue, "PgSqlHostDataSource::get6(id, address): "
3167 "wrong address type, address supplied is an IPv4 address");
3168 }
3169
3170 // Get a context
3171 PgSqlHostContextAlloc get_context(*impl_);
3172 PgSqlHostContextPtr ctx = get_context.ctx_;
3173
3174 // Set up the WHERE clause value
3175 PsqlBindArrayPtr bind_array(new PsqlBindArray());
3176
3177 // Add the subnet id
3178 bind_array->add(subnet_id);
3179
3180 // Add the prefix
3181 bind_array->add(address);
3182
3183 ConstHostCollection collection;
3184 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
3185 bind_array, ctx->host_ipv6_exchange_, collection, true);
3186
3187 // Return single record if present, else clear the host.
3188 ConstHostPtr result;
3189 if (!collection.empty()) {
3190 result = *collection.begin();
3191 }
3192
3193 return (result);
3194}
3195
3198 const asiolink::IOAddress& address) const {
3199 if (!address.isV6()) {
3200 isc_throw(BadValue, "PgSqlHostDataSource::get6(id, address): "
3201 "wrong address type, address supplied is an IPv4 address");
3202 }
3203
3204 // Get a context
3205 PgSqlHostContextAlloc get_context(*impl_);
3206 PgSqlHostContextPtr ctx = get_context.ctx_;
3207
3208 // Set up the WHERE clause value
3209 PsqlBindArrayPtr bind_array(new PsqlBindArray());
3210
3211 // Add the subnet id
3212 bind_array->add(subnet_id);
3213
3214 // Add the prefix
3215 bind_array->add(address);
3216
3217 ConstHostCollection collection;
3218 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
3219 bind_array, ctx->host_ipv6_exchange_, collection, false);
3220 return (collection);
3221}
3222
3225 if (!address.isV6()) {
3226 isc_throw(BadValue, "PgSqlHostDataSource::get6(address): "
3227 "wrong address type, address supplied is an IPv4 address");
3228 }
3229
3230 // Get a context
3231 PgSqlHostContextAlloc get_context(*impl_);
3232 PgSqlHostContextPtr ctx = get_context.ctx_;
3233
3234 // Set up the WHERE clause value
3235 PsqlBindArrayPtr bind_array(new PsqlBindArray());
3236
3237 // Add the prefix
3238 bind_array->add(address);
3239
3240 ConstHostCollection collection;
3241 impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_ADDR6,
3242 bind_array, ctx->host_ipv6_exchange_, collection, false);
3243 return (collection);
3244}
3245
3246void
3248 // Get a context.
3249 PgSqlHostContextAlloc const context(*impl_);
3250 PgSqlHostContextPtr ctx(context.ctx_);
3251
3252 // If operating in read-only mode, throw exception.
3253 impl_->checkReadOnly(ctx);
3254
3255 // Initiate PostgreSQL transaction as we will have to make multiple queries
3256 // to update host information into multiple tables. If that fails on
3257 // any stage, the transaction will be rolled back by the destructor of
3258 // the PgSqlTransaction class.
3259 PgSqlTransaction transaction(ctx->conn_);
3260
3261 // As much as having dedicated prepared statements for updating tables would be consistent with
3262 // the implementation of other commands, it's difficult if not impossible to cover all cases for
3263 // updating the host to exactly as is described in the command, which may involve inserts and
3264 // deletes alongside updates. So let's delete and add. The delete cascades into all tables. The
3265 // add explicitly adds into all tables.
3267
3268 // Everything went fine, so explicitly commit the transaction.
3269 transaction.commit();
3270}
3271
3272// Miscellaneous database methods.
3273
3274std::string
3276 std::string name = "";
3277 // Get a context
3278 PgSqlHostContextAlloc get_context(*impl_);
3279 PgSqlHostContextPtr ctx = get_context.ctx_;
3280
3281 try {
3282 name = ctx->conn_.getParameter("name");
3283 } catch (...) {
3284 // Return an empty name
3285 }
3286 return (name);
3287}
3288
3289std::string
3291 return (std::string("Host data source that stores host information"
3292 "in PostgreSQL database"));
3293}
3294
3295std::pair<uint32_t, uint32_t>
3296PgSqlHostDataSource::getVersion(const std::string& timer_name) const {
3297 return(impl_->getVersion(timer_name));
3298}
3299
3300void
3302 // Get a context
3303 PgSqlHostContextAlloc get_context(*impl_);
3304 PgSqlHostContextPtr ctx = get_context.ctx_;
3305
3306 // If operating in read-only mode, throw exception.
3307 impl_->checkReadOnly(ctx);
3308 ctx->conn_.commit();
3309}
3310
3311void
3313 // Get a context
3314 PgSqlHostContextAlloc get_context(*impl_);
3315 PgSqlHostContextPtr ctx = get_context.ctx_;
3316
3317 // If operating in read-only mode, throw exception.
3318 impl_->checkReadOnly(ctx);
3319 ctx->conn_.rollback();
3320}
3321
3322bool
3324 impl_->ip_reservations_unique_ = unique;
3325 return (true);
3326}
3327
3328bool
3330 return (impl_->unusable_);
3331}
3332
3333} // namespace dhcp
3334} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
static ElementPtr fromJSON(const std::string &in, bool preproc=false)
These functions will parse the given string (JSON) representation of a compound element.
Definition: data.cc:798
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:49
static bool invokeDbLostCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's lost connectivity callback.
static std::string redactedAccessString(const ParameterMap &parameters)
Redact database access string.
static bool invokeDbFailedCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restore failed connectivity callback.
static ParameterMap parse(const std::string &dbaccess)
Parse database access string.
static bool invokeDbRecoveredCallback(const util::ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restored connectivity callback.
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
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.
Database duplicate entry error.
Definition: db_exceptions.h:30
Multiple lease records found where one expected.
Definition: db_exceptions.h:16
Common PgSql Connector Pool.
static bool warned_about_tls
Emit the TLS support warning only once.
static const char DUPLICATE_KEY[]
Define the PgSql error state for a duplicate key error.
static void ensureSchemaVersion(const ParameterMap &parameters, const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string())
Retrieve schema version, validate it against the hardcoded version, and attempt to initialize the sch...
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap &parameters, const IOServiceAccessorPtr &ac=IOServiceAccessorPtr(), const DbCallback &cb=DbCallback(), const std::string &timer_name=std::string())
Get the schema version.
Base class for marshalling data to and from PostgreSQL.
static void convertFromBytea(const PgSqlResult &r, const int row, const size_t col, uint8_t *buffer, const size_t buffer_size, size_t &bytes_converted)
Converts a column in a row in a result set to a binary bytes.
static bool isColumnNull(const PgSqlResult &r, const int row, const size_t col)
Returns true if a column within a row is null.
static void getColumnValue(const PgSqlResult &r, const int row, const size_t col, std::string &value)
Fetches text column value as a string.
RAII wrapper for PostgreSQL Result sets.
int getRows() const
Returns the number of rows in the result set.
RAII object representing a PostgreSQL transaction.
void commit()
Commits transaction.
Attempt to modify data in read-only database.
Definition: db_exceptions.h:44
Authentication keys.
Definition: host.h:75
virtual void update(HostPtr const &host)
Attempts to update an existing host entry.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
static constexpr size_t MAX_CLIENT_ID_LEN
Maximum size of a client ID.
Definition: duid.h:235
static bool delBackend(const std::string &db_type)
Delete an alternate host backend (aka host data source).
Definition: host_mgr.cc:62
static void addBackend(const std::string &access)
Add an alternate host backend (aka host data source).
Definition: host_mgr.cc:57
Wraps value holding size of the page with host reservations.
const size_t page_size_
Holds page size.
Represents a device with IPv4 and/or IPv6 reservations.
Definition: host.h:297
IdentifierType
Type of the host identifier.
Definition: host.h:307
static const IdentifierType LAST_IDENTIFIER_TYPE
Constant pointing to the last identifier of the IdentifierType enumeration.
Definition: host.h:317
IPv6 reservation for a host.
Definition: host.h:161
const asiolink::IOAddress & getPrefix() const
Returns prefix for the reservation.
Definition: host.h:190
Type getType() const
Returns reservation type.
Definition: host.h:204
Type
Type of the reservation.
Definition: host.h:167
uint8_t getPrefixLen() const
Returns prefix length.
Definition: host.h:195
static OptionDefinitionPtr getOptionDef(const std::string &space, const uint16_t code)
Return the first option definition matching a particular option code.
Definition: libdhcp++.cc:126
static OptionDefinitionPtr getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id, const uint16_t code)
Returns vendor option definition for a given vendor-id and code.
Definition: libdhcp++.cc:168
static uint32_t optionSpaceToVendorId(const std::string &option_space)
Converts option space name to vendor id.
Definition: libdhcp++.cc:1298
static OptionDefinitionPtr getRuntimeOptionDef(const std::string &space, const uint16_t code)
Returns runtime (non-standard) option definition by space and option code.
Definition: libdhcp++.cc:189
static OptionDefinitionPtr getLastResortOptionDef(const std::string &space, const uint16_t code)
Returns last resort option definition by space and option code.
Definition: libdhcp++.cc:247
Option descriptor.
Definition: cfg_option.h:47
OptionPtr option_
Option instance.
Definition: cfg_option.h:50
bool cancelled_
Cancelled flag.
Definition: cfg_option.h:64
std::string formatted_value_
Option value in textual (CSV) format.
Definition: cfg_option.h:79
bool persistent_
Persistence flag.
Definition: cfg_option.h:56
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:83
PostgreSQL Host Context Pool.
std::mutex mutex_
The mutex to protect pool access.
std::vector< PgSqlHostContextPtr > pool_
The vector of available contexts.
boost::shared_ptr< PgSqlHostIPv6Exchange > host_ipv46_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
boost::shared_ptr< PgSqlIPv6ReservationExchange > host_ipv6_reservation_exchange_
Pointer to an object representing an exchange which can be used to insert new IPv6 reservation.
PgSqlHostContext(const DatabaseConnection::ParameterMap &parameters, IOServiceAccessorPtr io_service_accessor, db::DbCallback db_reconnect_callback)
Constructor.
PgSqlConnection conn_
PostgreSQL connection.
boost::shared_ptr< PgSqlHostWithOptionsExchange > host_ipv4_exchange_
The exchange objects are used for transfer of data to/from the database.
bool is_readonly_
Indicates if the database is opened in read only mode.
boost::shared_ptr< PgSqlHostIPv6Exchange > host_ipv6_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts,...
boost::shared_ptr< PgSqlOptionExchange > host_option_exchange_
Pointer to an object representing an exchange which can be used to insert DHCPv4 or DHCPv6 option int...
Implementation of the PgSqlHostDataSource.
bool ip_reservations_unique_
Holds the setting whether the IP reservations must be unique or may be non-unique.
uint64_t addStatement(PgSqlHostContextPtr &ctx, PgSqlHostDataSourceImpl::StatementIndex stindex, PsqlBindArrayPtr &bind, const bool return_last_id=false)
Executes statements which insert a row into one of the tables.
static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl)
Attempts to reconnect the server to the host DB backend manager.
std::pair< uint32_t, uint32_t > getVersion(const std::string &timer_name=std::string()) const
Returns PostgreSQL schema version of the open database.
void checkReadOnly(PgSqlHostContextPtr &ctx) const
Throws exception if database is read only.
DatabaseConnection::ParameterMap parameters_
The parameters.
PgSqlHostContextPoolPtr pool_
The pool of contexts.
void addOption(PgSqlHostContextPtr &ctx, const PgSqlHostDataSourceImpl::StatementIndex &stindex, const OptionDescriptor &opt_desc, const std::string &opt_space, const Optional< SubnetID > &subnet_id, const HostID &host_id)
Inserts a single DHCP option into the database.
void addOptions(PgSqlHostContextPtr &ctx, const StatementIndex &stindex, const ConstCfgOptionPtr &options_cfg, const uint64_t host_id)
Inserts multiple options into the database.
bool delStatement(PgSqlHostContextPtr &ctx, PgSqlHostDataSourceImpl::StatementIndex stindex, PsqlBindArrayPtr &bind)
Executes statements that delete records.
static const StatementIndex WRITE_STMTS_BEGIN
Index of first statement performing write to the database.
void addResv(PgSqlHostContextPtr &ctx, const IPv6Resrv &resv, const HostID &id)
Inserts IPv6 Reservation into ipv6_reservation table.
std::string timer_name_
Timer name used to register database reconnect timer.
ConstHostPtr getHost(PgSqlHostContextPtr &ctx, const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, StatementIndex stindex, boost::shared_ptr< PgSqlHostExchange > exchange) const
Retrieves a host by subnet and client's unique identifier.
PgSqlHostContextPtr createContext() const
Create a new context.
void getHostCollection(PgSqlHostContextPtr &ctx, StatementIndex stindex, PsqlBindArrayPtr bind, boost::shared_ptr< PgSqlHostExchange > exchange, ConstHostCollection &result, bool single) const
Creates collection of Host objects with associated information such as IPv6 reservations and/or DHCP ...
PgSqlHostDataSourceImpl(const DatabaseConnection::ParameterMap &parameters)
Constructor.
bool unusable_
Indicates if there is at least one connection that can no longer be used for normal operations.
PgSqlHostContextAlloc(PgSqlHostDataSourceImpl &mgr)
Constructor.
virtual bool del6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Attempts to delete a host by (subnet6-id, identifier type, identifier)
virtual ConstHostPtr get4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv4 subnet.
virtual bool isUnusable()
Flag which indicates if the host manager has at least one unusable connection.
virtual std::string getName() const
Returns the name of the open database.
virtual ConstHostCollection getAll(const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Return all hosts connected to any subnet for which reservations have been made using a specified iden...
virtual std::string getDescription() const
Returns description of the backend.
virtual ConstHostCollection getAllbyHostname4(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv4 subnet.
virtual ConstHostCollection getPage6(const SubnetID &subnet_id, size_t &source_index, uint64_t lower_host_id, const HostPageSize &page_size) const
Returns range of hosts in a DHCPv6 subnet.
virtual bool del(const SubnetID &subnet_id, const asiolink::IOAddress &addr)
Attempts to delete hosts by (subnet-id, address)
virtual ConstHostCollection getPage4(const SubnetID &subnet_id, size_t &source_index, uint64_t lower_host_id, const HostPageSize &page_size) const
Returns range of hosts in a DHCPv4 subnet.
virtual void rollback()
Rollback Transactions.
virtual bool del4(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len)
Attempts to delete a host by (subnet4-id, identifier type, identifier)
virtual ConstHostCollection getAll6(const SubnetID &subnet_id) const
Return all hosts in a DHCPv6 subnet.
virtual ConstHostCollection getAllbyHostname6(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv6 subnet.
virtual void commit()
Commit Transactions.
virtual bool setIPReservationsUnique(const bool unique)
Controls whether IP reservations are unique or non-unique.
virtual ConstHostCollection getAll4(const SubnetID &subnet_id) const
Return all hosts in a DHCPv4 subnet.
void update(HostPtr const &host)
Implements BaseHostDataSource::update() for PostgreSQL.
virtual isc::db::DatabaseConnection::ParameterMap getParameters() const
Return backend parameters.
PgSqlHostDataSource(const db::DatabaseConnection::ParameterMap &parameters)
Constructor.
virtual void add(const HostPtr &host)
Adds a new host to the collection.
virtual ConstHostPtr get6(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len) const
Returns a host connected to the IPv6 subnet.
virtual ~PgSqlHostDataSource()
Virtual destructor.
virtual ConstHostCollection getAllbyHostname(const std::string &hostname) const
Return all hosts with a hostname.
virtual std::pair< uint32_t, uint32_t > getVersion(const std::string &timer_name=std::string()) const
Returns backend version.
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition: timer_mgr.cc:446
RAII class creating a critical section.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
A template representing an optional value.
Definition: optional.h:36
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:347
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_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:29
const size_t OID_INT4
const size_t OID_INT2
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
const size_t OID_VARCHAR
boost::shared_ptr< PsqlBindArray > PsqlBindArrayPtr
Defines a smart pointer to PsqlBindArray.
const size_t OID_TEXT
const size_t OID_BOOL
const size_t OID_INT8
const size_t OID_BYTEA
std::function< bool(util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
std::function< isc::asiolink::IOServicePtr()> IOServiceAccessor
Function which returns the IOService that can be used to recover the connection.
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
boost::shared_ptr< PgSqlHostContext > PgSqlHostContextPtr
Type of pointers to contexts.
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:803
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition: host.h:807
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:813
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_GET_VERSION
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:243
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
const isc::log::MessageID DHCPSRV_PGSQL_NO_TLS_SUPPORT
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_RECONNECT_ATTEMPT_FAILED
const isc::log::MessageID DHCPSRV_PGSQL_TLS_SUPPORT
uint32_t SubnetID
Defines unique IPv4 or IPv6 subnet identifier.
Definition: subnet_id.h:25
uint64_t HostID
HostID (used only when storing in MySQL or PostgreSQL backends)
Definition: host.h:69
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:303
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_RECONNECT_ATTEMPT_SCHEDULE
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:810
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_RECONNECT_FAILED
const size_t OPTION_VALUE_MAX_LEN
Maximum length of option value.
Definition: host.h:45
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
boost::shared_ptr< PgSqlHostContextPool > PgSqlHostContextPoolPtr
Type of pointers to context pools.
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_READONLY
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:806
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
Defines the logger used by the top-level component of kea-lfc.
#define DHCP4_OPTION_SPACE
global std option spaces
#define DHCP6_OPTION_SPACE
data::ConstElementPtr getContext() const
Returns const pointer to the user context.
Definition: user_context.h:24
static const int BINARY_FMT
Format value for binary data.