Kea  1.9.9-git
pgsql_host_data_source.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <asiolink/io_service.h>
10 #include <database/db_exceptions.h>
11 #include <dhcp/libdhcp++.h>
12 #include <dhcp/option.h>
13 #include <dhcp/option_definition.h>
14 #include <dhcp/option_space.h>
15 #include <dhcpsrv/cfg_db_access.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/pointer_cast.hpp>
30 #include <boost/static_assert.hpp>
31 
32 #include <stdint.h>
33 
34 #include <mutex>
35 #include <string>
36 
37 using namespace isc;
38 using namespace isc::asiolink;
39 using namespace isc::db;
40 using namespace isc::dhcp;
41 using namespace isc::util;
42 using namespace isc::data;
43 using namespace std;
44 
45 namespace {
46 
50 const size_t OPTION_VALUE_MAX_LEN = 4096;
51 
56 const uint8_t MAX_IDENTIFIER_TYPE = static_cast<uint8_t>(Host::LAST_IDENTIFIER_TYPE);
57 
59 const size_t DHCP_IDENTIFIER_MAX_LEN = 128;
60 
87 class PgSqlHostExchange : public PgSqlExchange {
88 private:
89 
94  static const int HOST_ID_COL = 0;
95  static const int DHCP_IDENTIFIER_COL = 1;
96  static const int DHCP_IDENTIFIER_TYPE_COL = 2;
97  static const int DHCP4_SUBNET_ID_COL = 3;
98  static const int DHCP6_SUBNET_ID_COL = 4;
99  static const int IPV4_ADDRESS_COL = 5;
100  static const int HOSTNAME_COL = 6;
101  static const int DHCP4_CLIENT_CLASSES_COL = 7;
102  static const int DHCP6_CLIENT_CLASSES_COL = 8;
103  static const int USER_CONTEXT_COL = 9;
104  static const int DHCP4_NEXT_SERVER_COL = 10;
105  static const int DHCP4_SERVER_HOSTNAME_COL = 11;
106  static const int DHCP4_BOOT_FILE_NAME_COL = 12;
107  static const int AUTH_KEY_COL = 13;
109  static const size_t HOST_COLUMNS = 14;
110 
111 public:
112 
119  PgSqlHostExchange(const size_t additional_columns_num = 0)
120  : PgSqlExchange(HOST_COLUMNS + additional_columns_num) {
121  // Set the column names for use by this class. This only comprises
122  // names used by the PgSqlHostExchange class. Derived classes will
123  // need to set names for the columns they use. Currently these are
124  // only used for logging purposes.
125  columns_[HOST_ID_COL] = "host_id";
126  columns_[DHCP_IDENTIFIER_COL] = "dhcp_identifier";
127  columns_[DHCP_IDENTIFIER_TYPE_COL] = "dhcp_identifier_type";
128  columns_[DHCP4_SUBNET_ID_COL] = "dhcp4_subnet_id";
129  columns_[DHCP6_SUBNET_ID_COL] = "dhcp6_subnet_id";
130  columns_[IPV4_ADDRESS_COL] = "ipv4_address";
131  columns_[HOSTNAME_COL] = "hostname";
132  columns_[DHCP4_CLIENT_CLASSES_COL] = "dhcp4_client_classes";
133  columns_[DHCP6_CLIENT_CLASSES_COL] = "dhcp6_client_classes";
134  columns_[USER_CONTEXT_COL] = "user_context";
135  columns_[DHCP4_NEXT_SERVER_COL] = "dhcp4_next_server";
136  columns_[DHCP4_SERVER_HOSTNAME_COL] = "dhcp4_server_hostname";
137  columns_[DHCP4_BOOT_FILE_NAME_COL] = "dhcp4_boot_file_name";
138  columns_[AUTH_KEY_COL] = "auth_key";
139 
140  BOOST_STATIC_ASSERT(12 < HOST_COLUMNS);
141  };
142 
144  virtual ~PgSqlHostExchange() {
145  }
146 
152  virtual void clear() {
153  host_.reset();
154  };
155 
170  size_t findAvailColumn() const {
171  std::vector<std::string>::const_iterator empty_column =
172  std::find(columns_.begin(), columns_.end(), std::string());
173  return (std::distance(columns_.begin(), empty_column));
174  }
175 
180  HostID getHostId(const PgSqlResult& r, int row) {
181  HostID host_id;
182  getColumnValue(r, row, HOST_ID_COL, host_id);
183  return (host_id);
184  }
185 
201  PsqlBindArrayPtr createBindForSend(const HostPtr& host, const bool unique_ip) {
202  if (!host) {
203  isc_throw(BadValue, "createBindForSend:: host object is NULL");
204  }
205 
206  // Store the host to ensure bound values remain in scope
207  host_ = host;
208 
209  // Bind the host data to the array
210  PsqlBindArrayPtr bind_array(new PsqlBindArray());
211  try {
212  // host_id : is auto_incremented skip it
213 
214  // dhcp_identifier : BYTEA NOT NULL
215  bind_array->add(host->getIdentifier());
216 
217  // dhcp_identifier_type : SMALLINT NOT NULL
218  bind_array->add(host->getIdentifierType());
219 
220  // dhcp4_subnet_id : INT NULL
221  if (host->getIPv4SubnetID() == SUBNET_ID_UNUSED) {
222  bind_array->addNull();
223  }
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  }
232  else {
233  bind_array->add(host->getIPv6SubnetID());
234  }
235 
236  // ipv4_address : BIGINT NULL
237  bind_array->add((host->getIPv4Reservation()));
238 
239  // hostname : VARCHAR(255) NULL
240  bind_array->add(host->getHostname());
241 
242  // dhcp4_client_classes : VARCHAR(255) NULL
243  // Override default separator to not include space after comma.
244  bind_array->addTempString(host->getClientClasses4().toText(","));
245 
246  // dhcp6_client_classes : VARCHAR(255) NULL
247  bind_array->addTempString(host->getClientClasses6().toText(","));
248 
249  // user_context: TEXT NULL
250  ConstElementPtr ctx = host->getContext();
251  if (ctx) {
252  std::string user_context_ = ctx->str();
253  bind_array->addTempString(user_context_);
254  } else {
255  bind_array->addNull();
256  }
257 
258  // dhcp4_next_server : BIGINT NULL
259  bind_array->add((host->getNextServer()));
260 
261  // dhcp4_server_hostname : VARCHAR(64)
262  bind_array->add(host->getServerHostname());
263 
264  // dhcp4_boot_file_name : VARCHAR(128)
265  bind_array->add(host->getBootFileName());
266 
267  // add auth keys
268  std::string key = host->getKey().toText();
269  if (key.empty()) {
270  bind_array->addNull();
271  } else {
272  bind_array->add(key);
273  }
274 
275  // When checking whether the IP is unique we need to bind the IPv4 address
276  // at the end of the query as it has additional binding for the IPv4
277  // address.
278  if (unique_ip) {
279  bind_array->add(host->getIPv4Reservation()); // ipv4_address
280  bind_array->add(host->getIPv4SubnetID()); // subnet_id
281  }
282 
283 
284  } catch (const std::exception& ex) {
285  host_.reset();
287  "Could not create bind array from Host: "
288  << host->getHostname() << ", reason: " << ex.what());
289  }
290 
291  return (bind_array);
292  };
293 
307  virtual void processRowData(ConstHostCollection& hosts,
308  const PgSqlResult& r, int row) {
309  // Peek at the host id , so we can skip it if we already have it
310  // This lets us avoid constructing a copy of host for each
311  // of its sub-rows (options, etc...)
312  HostID row_host_id = getHostId(r, row);
313 
314  // Add new host only if there are no hosts or the host id of the
315  // most recently added host is different than the host id of the
316  // currently processed host.
317  if (hosts.empty() || row_host_id != hosts.back()->getHostId()) {
318  HostPtr host = retrieveHost(r, row, row_host_id);
319  hosts.push_back(host);
320  }
321  }
322 
334  HostPtr retrieveHost(const PgSqlResult& r, int row,
335  const HostID& peeked_host_id = 0) {
336 
337  // If the caller peeked ahead at the host_id use that, otherwise
338  // read it from the row.
339  HostID host_id = (peeked_host_id ? peeked_host_id : getHostId(r,row));
340 
341  // dhcp_identifier : BYTEA NOT NULL
342  uint8_t identifier_value[DHCP_IDENTIFIER_MAX_LEN];
343  size_t identifier_len;
344  convertFromBytea(r, row, DHCP_IDENTIFIER_COL, identifier_value,
345  sizeof(identifier_value), identifier_len);
346 
347  // dhcp_identifier_type : SMALLINT NOT NULL
348  uint8_t type;
349  getColumnValue(r, row, DHCP_IDENTIFIER_TYPE_COL, type);
350  if (type > MAX_IDENTIFIER_TYPE) {
351  isc_throw(BadValue, "invalid dhcp identifier type returned: "
352  << static_cast<int>(type));
353  }
354 
355  Host::IdentifierType identifier_type =
356  static_cast<Host::IdentifierType>(type);
357 
358  // dhcp4_subnet_id : INT NULL
359  uint32_t subnet_id(SUBNET_ID_UNUSED);
360  if (!isColumnNull(r, row, DHCP4_SUBNET_ID_COL)) {
361  getColumnValue(r, row, DHCP4_SUBNET_ID_COL, subnet_id);
362  }
363  SubnetID dhcp4_subnet_id = static_cast<SubnetID>(subnet_id);
364 
365  // dhcp6_subnet_id : INT NULL
366  subnet_id = SUBNET_ID_UNUSED;
367  if (!isColumnNull(r, row, DHCP6_SUBNET_ID_COL)) {
368  getColumnValue(r, row, DHCP6_SUBNET_ID_COL, subnet_id);
369  }
370  SubnetID dhcp6_subnet_id = static_cast<SubnetID>(subnet_id);
371 
372  // ipv4_address : BIGINT NULL
373  uint32_t addr4(0);
374  if (!isColumnNull(r, row, IPV4_ADDRESS_COL)) {
375  getColumnValue(r, row, IPV4_ADDRESS_COL, addr4);
376  }
377  isc::asiolink::IOAddress ipv4_reservation(addr4);
378 
379  // hostname : VARCHAR(255) NULL
380  std::string hostname;
381  if (!isColumnNull(r, row, HOSTNAME_COL)) {
382  getColumnValue(r, row, HOSTNAME_COL, hostname);
383  }
384 
385  // dhcp4_client_classes : VARCHAR(255) NULL
386  std::string dhcp4_client_classes;
387  if (!isColumnNull(r, row, DHCP4_CLIENT_CLASSES_COL)) {
388  getColumnValue(r, row, DHCP4_CLIENT_CLASSES_COL, dhcp4_client_classes);
389  }
390 
391  // dhcp6_client_classes : VARCHAR(255) NULL
392  std::string dhcp6_client_classes;
393  if (!isColumnNull(r, row, DHCP6_CLIENT_CLASSES_COL)) {
394  getColumnValue(r, row, DHCP6_CLIENT_CLASSES_COL, dhcp6_client_classes);
395  }
396 
397  // user_context: TEXT
398  std::string user_context;
399  if (!isColumnNull(r, row, USER_CONTEXT_COL)) {
400  getColumnValue(r, row, USER_CONTEXT_COL, user_context);
401  }
402 
403  // dhcp4_next_server : BIGINT NULL
404  uint32_t dhcp4_next_server_as_uint32(0);
405  if (!isColumnNull(r, row, DHCP4_NEXT_SERVER_COL)) {
406  getColumnValue(r, row, DHCP4_NEXT_SERVER_COL, dhcp4_next_server_as_uint32);
407  }
408  isc::asiolink::IOAddress dhcp4_next_server(dhcp4_next_server_as_uint32);
409 
410  // dhcp4_server_hostname : VARCHAR(64)
411  std::string dhcp4_server_hostname;
412  if (!isColumnNull(r, row, DHCP4_SERVER_HOSTNAME_COL)) {
413  getColumnValue(r, row, DHCP4_SERVER_HOSTNAME_COL, dhcp4_server_hostname);
414  }
415 
416  // dhcp4_boot_file_name : VARCHAR(128)
417  std::string dhcp4_boot_file_name;
418  if (!isColumnNull(r, row, DHCP4_BOOT_FILE_NAME_COL)) {
419  getColumnValue(r, row, DHCP4_BOOT_FILE_NAME_COL, dhcp4_boot_file_name);
420  }
421 
422  // auth_key : VARCHAR(16)
423  std::string auth_key;
424  if (!isColumnNull(r, row, AUTH_KEY_COL)) {
425  getColumnValue(r, row, AUTH_KEY_COL, auth_key);
426  }
427 
428  // Finally, attempt to create the new host.
429  HostPtr host;
430  try {
431  host.reset(new Host(identifier_value, identifier_len,
432  identifier_type, dhcp4_subnet_id,
433  dhcp6_subnet_id, ipv4_reservation, hostname,
434  dhcp4_client_classes, dhcp6_client_classes,
435  dhcp4_next_server, dhcp4_server_hostname,
436  dhcp4_boot_file_name, AuthKey(auth_key)));
437 
438  // Set the user context if there is one.
439  if (!user_context.empty()) {
440  try {
441  ConstElementPtr ctx = Element::fromJSON(user_context);
442  if (!ctx || (ctx->getType() != Element::map)) {
443  isc_throw(BadValue, "user context '" << user_context
444  << "' is not a JSON map");
445  }
446  host->setContext(ctx);
447  } catch (const isc::data::JSONError& ex) {
448  isc_throw(BadValue, "user context '" << user_context
449  << "' is invalid JSON: " << ex.what());
450  }
451  }
452 
453  host->setHostId(host_id);
454  } catch (const isc::Exception& ex) {
455  isc_throw(DbOperationError, "Could not create host: " << ex.what());
456  }
457 
458  return(host);
459  };
460 
461 protected:
464  HostPtr host_;
465 };
466 
476 class PgSqlHostWithOptionsExchange : public PgSqlHostExchange {
477 private:
478 
480  static const size_t OPTION_COLUMNS = 7;
481 
496  class OptionProcessor {
497  public:
498 
505  OptionProcessor(const Option::Universe& universe,
506  const size_t start_column)
507  : universe_(universe), start_column_(start_column),
508  option_id_index_(start_column), code_index_(start_column_ + 1),
509  value_index_(start_column_ + 2),
510  formatted_value_index_(start_column_ + 3),
511  space_index_(start_column_ + 4),
512  persistent_index_(start_column_ + 5),
513  user_context_index_(start_column_ + 6),
514  most_recent_option_id_(0) {
515  }
516 
521  void clear() {
522  most_recent_option_id_ = 0;
523  }
524 
557  void retrieveOption(const CfgOptionPtr& cfg, const PgSqlResult& r,
558  int row) {
559  // If the option id on this row is NULL, then there's no
560  // option of this type (4/6) on this row to fetch, so bail.
561  if (PgSqlExchange::isColumnNull(r, row, option_id_index_)) {
562  return;
563  }
564 
565  // option_id: INT
566  uint64_t option_id;
567  PgSqlExchange::getColumnValue(r, row, option_id_index_, option_id);
568 
569  // The row option id must be greater than id if the most recent
570  // option because they are ordered by option id. Otherwise
571  // we assume that we have already processed this option.
572  if (most_recent_option_id_ >= option_id) {
573  return;
574  }
575 
576  // Remember current option id as the most recent processed one. We
577  // will be comparing it with option ids in subsequent rows.
578  most_recent_option_id_ = option_id;
579 
580  // code: SMALLINT NOT NULL
581  uint16_t code;
582  PgSqlExchange::getColumnValue(r, row, code_index_, code);
583 
584  // value: BYTEA
585  uint8_t value[OPTION_VALUE_MAX_LEN];
586  size_t value_len(0);
587  if (!isColumnNull(r, row, value_index_)) {
588  PgSqlExchange::convertFromBytea(r, row, value_index_, value,
589  sizeof(value), value_len);
590  }
591 
592  // formatted_value: TEXT
593  std::string formatted_value;
594  if (!isColumnNull(r, row, formatted_value_index_)) {
595  PgSqlExchange::getColumnValue(r, row, formatted_value_index_,
596  formatted_value);
597  }
598 
599  // space: VARCHAR(128)
600  std::string space;
601  if (!isColumnNull(r, row, space_index_)) {
602  PgSqlExchange::getColumnValue(r, row, space_index_, space);
603  }
604 
605  // If empty or null space provided, use a default top level space.
606  if (space.empty()) {
607  space = (universe_ == Option::V4 ?
609  }
610 
611  // persistent: BOOL default false
612  bool persistent;
613  PgSqlExchange::getColumnValue(r, row, persistent_index_,
614  persistent);
615 
616  // user_context: TEXT
617  std::string user_context;
618  if (!isColumnNull(r, row, user_context_index_)) {
619  PgSqlExchange::getColumnValue(r, row, user_context_index_,
620  user_context);
621  }
622 
623  // Options are held in a binary or textual format in the database.
624  // This is similar to having an option specified in a server
625  // configuration file. Such option is converted to appropriate C++
626  // class, using option definition. Thus, we need to find the
627  // option definition for this option code and option space.
628 
629  // If the option space is a standard DHCPv4 or DHCPv6 option space,
630  // this is most likely a standard option, for which we have a
631  // definition created within libdhcp++.
632  OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code);
633 
634  // Otherwise, we may check if this an option encapsulated within the
635  // vendor space.
636  if (!def && (space != DHCP4_OPTION_SPACE) &&
637  (space != DHCP6_OPTION_SPACE)) {
638  uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(space);
639  if (vendor_id > 0) {
640  def = LibDHCP::getVendorOptionDef(universe_, vendor_id,
641  code);
642  }
643  }
644 
645  // In all other cases, we use runtime option definitions, which
646  // should be also registered within the libdhcp++.
647  if (!def) {
648  def = LibDHCP::getRuntimeOptionDef(space, code);
649  }
650 
651  OptionPtr option;
652 
653  if (!def) {
654  // If no definition found, we use generic option type.
655  OptionBuffer buf(value, value + value_len);
656  option.reset(new Option(universe_, code, buf.begin(),
657  buf.end()));
658  } else {
659  // The option value may be specified in textual or binary format
660  // in the database. If formatted_value is empty, the binary
661  // format is used. Depending on the format we use a different
662  // variant of the optionFactory function.
663  if (formatted_value.empty()) {
664  OptionBuffer buf(value, value + value_len);
665  option = def->optionFactory(universe_, code, buf.begin(),
666  buf.end());
667  } else {
668  // Spit the value specified in comma separated values
669  // format.
670  std::vector<std::string> split_vec;
671  boost::split(split_vec, formatted_value,
672  boost::is_any_of(","));
673  option = def->optionFactory(universe_, code, split_vec);
674  }
675  }
676 
677  OptionDescriptor desc(option, persistent, formatted_value);
678 
679  // Set the user context if there is one into the option descriptor.
680  if (!user_context.empty()) {
681  try {
682  ConstElementPtr ctx = Element::fromJSON(user_context);
683  if (!ctx || (ctx->getType() != Element::map)) {
684  isc_throw(BadValue, "user context '" << user_context
685  << "' is no a JSON map");
686  }
687  desc.setContext(ctx);
688  } catch (const isc::data::JSONError& ex) {
689  isc_throw(BadValue, "user context '" << user_context
690  << "' is invalid JSON: " << ex.what());
691  }
692  }
693 
694  cfg->add(desc, space);
695  }
696 
701  void setColumnNames(std::vector<std::string>& columns) {
702  columns[option_id_index_] = "option_id";
703  columns[code_index_] = "code";
704  columns[value_index_] = "value";
705  columns[formatted_value_index_] = "formatted_value";
706  columns[space_index_] = "space";
707  columns[persistent_index_] = "persistent";
708  columns[user_context_index_] = "user_context";
709  }
710 
711  private:
713  Option::Universe universe_;
714 
716  size_t start_column_;
717 
719 
721 
722  size_t option_id_index_;
724 
726  size_t code_index_;
727 
729  size_t value_index_;
730 
732  size_t formatted_value_index_;
733 
735  size_t space_index_;
736 
738  size_t persistent_index_;
740 
742  size_t user_context_index_;
743 
745  uint64_t most_recent_option_id_;
746  };
747 
749  typedef boost::shared_ptr<OptionProcessor> OptionProcessorPtr;
750 
751 public:
752 
759  enum FetchedOptions {
760  DHCP4_ONLY,
761  DHCP6_ONLY,
762  DHCP4_AND_DHCP6
763  };
764 
773  PgSqlHostWithOptionsExchange(const FetchedOptions& fetched_options,
774  const size_t additional_columns_num = 0)
775  : PgSqlHostExchange(getRequiredColumnsNum(fetched_options)
776  + additional_columns_num),
777  opt_proc4_(), opt_proc6_() {
778 
779  // Create option processor for DHCPv4 options, if required.
780  if ((fetched_options == DHCP4_ONLY) ||
781  (fetched_options == DHCP4_AND_DHCP6)) {
782  opt_proc4_.reset(new OptionProcessor(Option::V4,
783  findAvailColumn()));
784  opt_proc4_->setColumnNames(columns_);
785  }
786 
787  // Create option processor for DHCPv6 options, if required.
788  if ((fetched_options == DHCP6_ONLY) ||
789  (fetched_options == DHCP4_AND_DHCP6)) {
790  opt_proc6_.reset(new OptionProcessor(Option::V6,
791  findAvailColumn()));
792  opt_proc6_->setColumnNames(columns_);
793  }
794  }
795 
801  virtual void clear() {
802  PgSqlHostExchange::clear();
803  if (opt_proc4_) {
804  opt_proc4_->clear();
805  }
806 
807  if (opt_proc6_) {
808  opt_proc6_->clear();
809  }
810  }
811 
821  virtual void processRowData(ConstHostCollection& hosts,
822  const PgSqlResult& r, int row) {
823  HostPtr current_host;
824  if (hosts.empty()) {
825  // Must be the first one, fetch it.
826  current_host = retrieveHost(r, row);
827  hosts.push_back(current_host);
828  } else {
829  // Peek at the host id so we can skip it if we already have
830  // this host. This lets us avoid retrieving the host needlessly
831  // for each of its sub-rows (options, etc...).
832  HostID row_host_id = getHostId(r, row);
833  current_host = boost::const_pointer_cast<Host>(hosts.back());
834 
835  // if the row's host id is greater than the one we've been
836  // working on we're starting a new host, so fetch it.
837  if (row_host_id > current_host->getHostId()) {
838  current_host = retrieveHost(r, row, row_host_id);
839  hosts.push_back(current_host);
840  }
841  }
842 
843  // Parse DHCPv4 options if required to do so.
844  if (opt_proc4_) {
845  CfgOptionPtr cfg = current_host->getCfgOption4();
846  opt_proc4_->retrieveOption(cfg, r, row);
847  }
848 
849  // Parse DHCPv6 options if required to do so.
850  if (opt_proc6_) {
851  CfgOptionPtr cfg = current_host->getCfgOption6();
852  opt_proc6_->retrieveOption(cfg, r, row);
853  }
854  }
855 
856 private:
857 
869  static size_t getRequiredColumnsNum(const FetchedOptions& fetched_options) {
870  return (fetched_options == DHCP4_AND_DHCP6 ? 2 * OPTION_COLUMNS :
871  OPTION_COLUMNS);
872  }
873 
877  OptionProcessorPtr opt_proc4_;
878 
882  OptionProcessorPtr opt_proc6_;
883 };
884 
897 class PgSqlHostIPv6Exchange : public PgSqlHostWithOptionsExchange {
898 private:
899 
901  static const size_t RESERVATION_COLUMNS = 5;
902 
903 public:
904 
909  PgSqlHostIPv6Exchange(const FetchedOptions& fetched_options)
910  : PgSqlHostWithOptionsExchange(fetched_options, RESERVATION_COLUMNS),
911  reservation_id_index_(findAvailColumn()),
912  address_index_(reservation_id_index_ + 1),
913  prefix_len_index_(reservation_id_index_ + 2),
914  type_index_(reservation_id_index_ + 3),
915  iaid_index_(reservation_id_index_ + 4),
916  most_recent_reservation_id_(0) {
917 
918  // Provide names of additional columns returned by the queries.
919  columns_[reservation_id_index_] = "reservation_id";
920  columns_[address_index_] = "address";
921  columns_[prefix_len_index_] = "prefix_len";
922  columns_[type_index_] = "type";
923  columns_[iaid_index_] = "dhcp6_iaid";
924 
925  BOOST_STATIC_ASSERT(4 < RESERVATION_COLUMNS);
926  }
927 
933  void clear() {
934  PgSqlHostWithOptionsExchange::clear();
935  most_recent_reservation_id_ = 0;
936  }
937 
941  uint64_t getReservationId(const PgSqlResult& r, int row) const {
942  uint64_t resv_id = 0;
943  if (!isColumnNull(r, row, reservation_id_index_)) {
944  getColumnValue(r, row, reservation_id_index_, resv_id);
945  }
946 
947  return (resv_id);
948  };
949 
954  IPv6Resrv retrieveReservation(const PgSqlResult& r, int row) {
955 
956  // type: SMALLINT NOT NULL
957  uint16_t tmp;
958  getColumnValue(r, row, type_index_, tmp);
959 
960  // Convert it to IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
961  IPv6Resrv::Type resv_type;
962  switch (tmp) {
963  case 0:
964  resv_type = IPv6Resrv::TYPE_NA;
965  break;
966 
967  case 2:
968  resv_type = IPv6Resrv::TYPE_PD;
969  break;
970 
971  default:
973  "invalid IPv6 reservation type returned: "
974  << tmp << ". Only 0 or 2 are allowed.");
975  }
976 
977  // address VARCHAR(39) NOT NULL
978  isc::asiolink::IOAddress address(getIPv6Value(r, row, address_index_));
979 
980  // prefix_len: SMALLINT NOT NULL
981  uint16_t prefix_len;
982  getColumnValue(r, row, prefix_len_index_, prefix_len);
983 
984  // @todo once we support populating iaid
985  // iaid: INT
986  // int iaid;
987  // getColumnValue(r, row, iaid_index_, iaid);
988 
989  // Create the reservation.
990  IPv6Resrv reservation(resv_type, IOAddress(address), prefix_len);
991  return (reservation);
992  };
993 
1015  virtual void processRowData(ConstHostCollection& hosts,
1016  const PgSqlResult& r, int row) {
1017  // Call parent class to fetch host information and options.
1018  PgSqlHostWithOptionsExchange::processRowData(hosts, r, row);
1019 
1020  // Shouldn't happen but just in case
1021  if (hosts.empty()) {
1022  isc_throw(Unexpected, "no host information while retrieving"
1023  " IPv6 reservation");
1024  }
1025 
1026  // If we have reservation id we haven't seen yet, retrieve the
1027  // the reservation, adding it to the current host
1028  uint64_t reservation_id = getReservationId(r, row);
1029  if (reservation_id && (reservation_id > most_recent_reservation_id_)) {
1030  HostPtr host = boost::const_pointer_cast<Host>(hosts.back());
1031  host->addReservation(retrieveReservation(r, row));
1032  most_recent_reservation_id_ = reservation_id;
1033  }
1034  }
1035 
1036 private:
1038 
1039  size_t reservation_id_index_;
1041 
1043  size_t address_index_;
1044 
1046  size_t prefix_len_index_;
1047 
1049  size_t type_index_;
1050 
1052  size_t iaid_index_;
1053 
1055 
1057  uint64_t most_recent_reservation_id_;
1058 };
1059 
1070 class PgSqlIPv6ReservationExchange : public PgSqlExchange {
1071 private:
1072 
1074  static const size_t RESRV_COLUMNS = 6;
1075 
1076 public:
1077 
1081  PgSqlIPv6ReservationExchange()
1082  : PgSqlExchange(RESRV_COLUMNS),
1083  resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
1084  // Set the column names (for error messages)
1085  columns_[0] = "host_id";
1086  columns_[1] = "address";
1087  columns_[2] = "prefix_len";
1088  columns_[3] = "type";
1089  columns_[4] = "dhcp6_iaid";
1090  BOOST_STATIC_ASSERT(5 < RESRV_COLUMNS);
1091  }
1092 
1107  PsqlBindArrayPtr createBindForSend(const IPv6Resrv& resv,
1108  const HostID& host_id,
1109  const bool unique_ip) {
1110  // Store the values to ensure they remain valid.
1111  // Technically we don't need this, as currently all the values
1112  // are converted to strings and stored by the bind array.
1113  resv_ = resv;
1114 
1115  PsqlBindArrayPtr bind_array(new PsqlBindArray());
1116 
1117  try {
1118  // address VARCHAR(39) NOT NULL
1119  bind_array->add(resv.getPrefix());
1120 
1121  // prefix_len: SMALLINT NOT NULL
1122  bind_array->add(resv.getPrefixLen());
1123 
1124  // type: SMALLINT NOT NULL
1125  // See lease6_types table for values (0 = IA_NA, 2 = IA_PD)
1126  uint16_t type = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
1127  bind_array->add(type);
1128 
1129  // dhcp6_iaid: INT UNSIGNED
1131  bind_array->addNull();
1132 
1133  // host_id: BIGINT NOT NULL
1134  bind_array->add(host_id);
1135 
1136  // When checking whether the IP is unique we need to bind the IPv6 address
1137  // and prefix length at the end of the query as it has additional binding
1138  // for the IPv6 address and prefix length.
1139  if (unique_ip) {
1140  bind_array->add(resv.getPrefix()); // address
1141  bind_array->add(resv.getPrefixLen()); // prefix_len
1142  }
1143  } catch (const std::exception& ex) {
1145  "Could not create bind array from IPv6 Reservation: "
1146  << resv_.toText() << ", reason: " << ex.what());
1147  }
1148 
1149  return (bind_array);
1150  }
1151 
1152 private:
1154  IPv6Resrv resv_;
1155 };
1156 
1160 class PgSqlOptionExchange : public PgSqlExchange {
1161 private:
1162 
1163  static const int OPTION_ID_COL = 0;
1164  static const int CODE_COL = 1;
1165  static const int VALUE_COL = 2;
1166  static const int FORMATTED_VALUE_COL = 3;
1167  static const int SPACE_COL = 4;
1168  static const int PERSISTENT_COL = 5;
1169  static const int USER_CONTEXT_COL = 6;
1170  static const int DHCP_CLIENT_CLASS_COL = 7;
1171  static const int DHCP_SUBNET_ID_COL = 8;
1172  static const int HOST_ID_COL = 9;
1173  static const int SCOPE_ID_COL = 10;
1174 
1176  static const size_t OPTION_COLUMNS = 11;
1177 
1178 public:
1179 
1181  PgSqlOptionExchange()
1182  : PgSqlExchange(OPTION_COLUMNS), value_(),
1183  value_len_(0), option_() {
1184  columns_[OPTION_ID_COL] = "option_id";
1185  columns_[CODE_COL] = "code";
1186  columns_[VALUE_COL] = "value";
1187  columns_[FORMATTED_VALUE_COL] = "formatted_value";
1188  columns_[SPACE_COL] = "space";
1189  columns_[PERSISTENT_COL] = "persistent";
1190  columns_[USER_CONTEXT_COL] = "user_context";
1191  columns_[DHCP_CLIENT_CLASS_COL] = "dhcp_client_class";
1192  columns_[DHCP_SUBNET_ID_COL] = "dhcp_subnet_id";
1193  columns_[HOST_ID_COL] = "host_id";
1194  columns_[SCOPE_ID_COL] = "scope_id";
1195 
1196  BOOST_STATIC_ASSERT(10 < OPTION_COLUMNS);
1197  }
1198 
1207  PsqlBindArrayPtr createBindForSend(const OptionDescriptor& opt_desc,
1208  const std::string& opt_space,
1209  const HostID& host_id) {
1210  // Hold pointer to the option to make sure it remains valid until
1211  // we complete a query.
1212  option_ = opt_desc.option_;
1213 
1214  // Create the bind-array
1215  PsqlBindArrayPtr bind_array(new PsqlBindArray());
1216 
1217  try {
1218  // option_id: is auto_incremented so skip it
1219 
1220  // code: SMALLINT UNSIGNED NOT NULL
1221  bind_array->add(option_->getType());
1222 
1223  // value: BYTEA NULL
1224  if (opt_desc.formatted_value_.empty() &&
1225  (opt_desc.option_->len() > opt_desc.option_->getHeaderLen())) {
1226  // The formatted_value is empty and the option value is
1227  // non-empty so we need to prepare on-wire format for the
1228  // option and store it in the database as a BYTEA.
1229  OutputBuffer buf(opt_desc.option_->len());
1230  opt_desc.option_->pack(buf);
1231  const char* buf_ptr = static_cast<const char*>(buf.getData());
1232  value_.assign(buf_ptr + opt_desc.option_->getHeaderLen(),
1233  buf_ptr + buf.getLength());
1234  value_len_ = value_.size();
1235  bind_array->add(value_);
1236  } else {
1237  // No value or formatted_value specified. In this case, the
1238  // value BYTEA should be NULL.
1239  bind_array->addNull(PsqlBindArray::BINARY_FMT);
1240  }
1241 
1242  // formatted_value: TEXT NULL,
1243  if (!opt_desc.formatted_value_.empty()) {
1244  bind_array->addTempString(opt_desc.formatted_value_);
1245  } else {
1246  bind_array->addNull();
1247  }
1248 
1249  // space: VARCHAR(128) NULL
1250  if (!opt_space.empty()) {
1251  bind_array->addTempString(opt_space);
1252  } else {
1253  bind_array->addNull();
1254  }
1255 
1256  // persistent: BOOLEAN DEFAULT false
1257  bind_array->add(opt_desc.persistent_);
1258 
1259  // user_context: TEXT NULL,
1260  ConstElementPtr ctx = opt_desc.getContext();
1261  if (ctx) {
1262  std::string user_context_ = ctx->str();
1263  bind_array->addTempString(user_context_);
1264  } else {
1265  bind_array->addNull();
1266  }
1267 
1268  // host_id: INT NULL
1269  if (!host_id) {
1270  isc_throw(BadValue, "host_id cannot be null");
1271  }
1272  bind_array->add(host_id);
1273 
1274  } catch (const std::exception& ex) {
1276  "Could not create bind array for inserting DHCP "
1277  "host option: " << option_->toText() << ", reason: "
1278  << ex.what());
1279  }
1280 
1281  return (bind_array);
1282  }
1283 
1284 private:
1285 
1287  std::vector<uint8_t> value_;
1288 
1290  size_t value_len_;
1291 
1293  OptionPtr option_;
1294 };
1295 
1296 } // namespace
1297 
1298 namespace isc {
1299 namespace dhcp {
1300 
1311 public:
1312 
1319  IOServiceAccessorPtr io_service_accessor,
1320  db::DbCallback db_reconnect_callback);
1321 
1326 
1329  boost::shared_ptr<PgSqlHostWithOptionsExchange> host_ipv4_exchange_;
1330 
1333  boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv6_exchange_;
1334 
1338  boost::shared_ptr<PgSqlHostIPv6Exchange> host_ipv46_exchange_;
1339 
1342  boost::shared_ptr<PgSqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
1343 
1347  boost::shared_ptr<PgSqlOptionExchange> host_option_exchange_;
1348 
1351 
1354 };
1355 
1363 public:
1364 
1366  std::vector<PgSqlHostContextPtr> pool_;
1367 
1369  std::mutex mutex_;
1370 };
1371 
1373 typedef boost::shared_ptr<PgSqlHostContextPool> PgSqlHostContextPoolPtr;
1374 
1377 public:
1378 
1388  GET_HOST_DHCPID, // Gets hosts by host identifier
1389  GET_HOST_ADDR, // Gets hosts by IPv4 address
1390  GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
1391  GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
1392  GET_HOST_SUBID_ADDR, // Gets host by IPv4 SubnetID and IPv4 address
1393  GET_HOST_PREFIX, // Gets host by IPv6 prefix
1394  GET_HOST_SUBID6_ADDR, // Gets host by IPv6 SubnetID and IPv6 prefix
1395  GET_HOST_SUBID4, // Gets hosts by IPv4 SubnetID
1396  GET_HOST_SUBID6, // Gets hosts by IPv6 SubnetID
1397  GET_HOST_HOSTNAME, // Gets hosts by hostname
1398  GET_HOST_HOSTNAME_SUBID4, // Gets hosts by hostname and IPv4 SubnetID
1399  GET_HOST_HOSTNAME_SUBID6, // Gets hosts by hostname and IPv6 SubnetID
1400  GET_HOST_SUBID4_PAGE, // Gets hosts by IPv4 SubnetID beginning by HID
1401  GET_HOST_SUBID6_PAGE, // Gets hosts by IPv6 SubnetID beginning by HID
1402  GET_HOST_PAGE4, // Gets v4 hosts beginning by HID
1403  GET_HOST_PAGE6, // Gets v6 hosts beginning by HID
1404  INSERT_HOST_NON_UNIQUE_IP, // Insert new host to collection with allowing IP duplicates
1405  INSERT_HOST_UNIQUE_IP, // Insert new host to collection with checking for IP duplicates
1406  INSERT_V6_RESRV_NON_UNIQUE,// Insert v6 reservation without checking that it is unique
1407  INSERT_V6_RESRV_UNIQUE, // Insert v6 reservation with checking that it is unique
1408  INSERT_V4_HOST_OPTION, // Insert DHCPv4 option
1409  INSERT_V6_HOST_OPTION, // Insert DHCPv6 option
1410  DEL_HOST_ADDR4, // Delete v4 host (subnet-id, addr4)
1411  DEL_HOST_ADDR6, // Delete v6 host (subnet-id, addr6)
1412  DEL_HOST_SUBID4_ID, // Delete v4 host (subnet-id, ident.type, identifier)
1413  DEL_HOST_SUBID6_ID, // Delete v6 host (subnet-id, ident.type, identifier)
1414  NUM_STATEMENTS // Number of statements
1415  };
1416 
1422  static const StatementIndex WRITE_STMTS_BEGIN = INSERT_HOST_NON_UNIQUE_IP;
1423 
1429 
1432 
1455  static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl);
1456 
1466  PgSqlHostContextPtr createContext() const;
1467 
1484  uint64_t addStatement(PgSqlHostContextPtr& ctx,
1486  PsqlBindArrayPtr& bind,
1487  const bool return_last_id = false);
1488 
1496  bool delStatement(PgSqlHostContextPtr& ctx,
1498  PsqlBindArrayPtr& bind);
1499 
1505  void addResv(PgSqlHostContextPtr& ctx,
1506  const IPv6Resrv& resv,
1507  const HostID& id);
1508 
1518  void addOption(PgSqlHostContextPtr& ctx,
1520  const OptionDescriptor& opt_desc,
1521  const std::string& opt_space,
1522  const Optional<SubnetID>& subnet_id,
1523  const HostID& host_id);
1524 
1533  void addOptions(PgSqlHostContextPtr& ctx,
1534  const StatementIndex& stindex,
1535  const ConstCfgOptionPtr& options_cfg,
1536  const uint64_t host_id);
1537 
1556  void getHostCollection(PgSqlHostContextPtr& ctx,
1557  StatementIndex stindex,
1558  PsqlBindArrayPtr bind,
1559  boost::shared_ptr<PgSqlHostExchange> exchange,
1560  ConstHostCollection& result,
1561  bool single) const;
1562 
1580  ConstHostPtr getHost(PgSqlHostContextPtr& ctx,
1581  const SubnetID& subnet_id,
1582  const Host::IdentifierType& identifier_type,
1583  const uint8_t* identifier_begin,
1584  const size_t identifier_len,
1585  StatementIndex stindex,
1586  boost::shared_ptr<PgSqlHostExchange> exchange) const;
1587 
1597  void checkReadOnly(PgSqlHostContextPtr& ctx) const;
1598 
1607  std::pair<uint32_t, uint32_t> getVersion() const;
1608 
1611 
1615 
1617  PgSqlHostContextPoolPtr pool_;
1618 
1622 
1624  std::string timer_name_;
1625 };
1626 
1627 namespace {
1628 
1630 typedef boost::array<PgSqlTaggedStatement, PgSqlHostDataSourceImpl::NUM_STATEMENTS>
1631 TaggedStatementArray;
1632 
1635 TaggedStatementArray tagged_statements = { {
1636  // PgSqlHostDataSourceImpl::GET_HOST_DHCPID
1637  // Retrieves host information, IPv6 reservations and both DHCPv4 and
1638  // DHCPv6 options associated with the host. The LEFT JOIN clause is used
1639  // to retrieve information from 4 different tables using a single query.
1640  // Hence, this query returns multiple rows for a single host.
1641  {2,
1642  { OID_BYTEA, OID_INT2 },
1643  "get_host_dhcpid",
1644  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1645  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
1646  " h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
1647  " h.user_context, "
1648  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1649  " h.dhcp4_boot_file_name, h.auth_key, "
1650  " o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
1651  " o4.persistent, o4.user_context, "
1652  " o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
1653  " o6.persistent, o6.user_context, "
1654  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
1655  "FROM hosts AS h "
1656  "LEFT JOIN dhcp4_options AS o4 ON h.host_id = o4.host_id "
1657  "LEFT JOIN dhcp6_options AS o6 ON h.host_id = o6.host_id "
1658  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1659  "WHERE dhcp_identifier = $1 AND dhcp_identifier_type = $2 "
1660  "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"
1661  },
1662 
1663  // PgSqlHostDataSourceImpl::GET_HOST_ADDR
1664  // Retrieves host information along with the DHCPv4 options associated with
1665  // it. Left joining the dhcp4_options table results in multiple rows being
1666  // returned for the same host. The host is retrieved by IPv4 address.
1667  {1,
1668  { OID_INT8 },
1669  "get_host_addr",
1670  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1671  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1672  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1673  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1674  " h.dhcp4_boot_file_name, h.auth_key, "
1675  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1676  " o.persistent, o.user_context "
1677  "FROM hosts AS h "
1678  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1679  "WHERE ipv4_address = $1 "
1680  "ORDER BY h.host_id, o.option_id"
1681  },
1682 
1683  // PgSqlHostDataSourceImpl::GET_HOST_SUBID4_DHCPID
1684  // Retrieves host information and DHCPv4 options using subnet identifier
1685  // and client's identifier. Left joining the dhcp4_options table results in
1686  // multiple rows being returned for the same host.
1687  {3,
1688  { OID_INT8, OID_INT2, OID_BYTEA },
1689  "get_host_subid4_dhcpid",
1690  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1691  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1692  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1693  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1694  " h.dhcp4_boot_file_name, h.auth_key, "
1695  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1696  " o.persistent, o.user_context "
1697  "FROM hosts AS h "
1698  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1699  "WHERE h.dhcp4_subnet_id = $1 AND h.dhcp_identifier_type = $2 "
1700  " AND h.dhcp_identifier = $3 "
1701  "ORDER BY h.host_id, o.option_id"
1702  },
1703 
1704  // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_DHCPID
1705  // Retrieves host information, IPv6 reservations and DHCPv6 options
1706  // associated with a host. The number of rows returned is a multiplication
1707  // of number of IPv6 reservations and DHCPv6 options.
1708  {3,
1709  { OID_INT8, OID_INT2, OID_BYTEA },
1710  "get_host_subid6_dhcpid",
1711  "SELECT h.host_id, h.dhcp_identifier, "
1712  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1713  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1714  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1715  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1716  " h.dhcp4_boot_file_name, h.auth_key, "
1717  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1718  " o.persistent, o.user_context, "
1719  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
1720  "FROM hosts AS h "
1721  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1722  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1723  "WHERE h.dhcp6_subnet_id = $1 AND h.dhcp_identifier_type = $2 "
1724  " AND h.dhcp_identifier = $3 "
1725  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1726  },
1727 
1728  // PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR
1729  // Retrieves host information and DHCPv4 options for the host using subnet
1730  // identifier and IPv4 reservation. Left joining the dhcp4_options table
1731  // results in multiple rows being returned for the host. The number of
1732  // rows depends on the number of options defined for the host.
1733  {2,
1734  { OID_INT8, OID_INT8 },
1735  "get_host_subid_addr",
1736  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1737  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1738  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1739  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1740  " h.dhcp4_boot_file_name, h.auth_key, "
1741  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1742  " o.persistent, o.user_context "
1743  "FROM hosts AS h "
1744  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1745  "WHERE h.dhcp4_subnet_id = $1 AND h.ipv4_address = $2 "
1746  "ORDER BY h.host_id, o.option_id"
1747  },
1748 
1749  // PgSqlHostDataSourceImpl::GET_HOST_PREFIX
1750  // Retrieves host information, IPv6 reservations and DHCPv6 options
1751  // associated with a host using prefix and prefix length. This query
1752  // returns host information for a single host. However, multiple rows
1753  // are returned due to left joining IPv6 reservations and DHCPv6 options.
1754  // The number of rows returned is multiplication of number of existing
1755  // IPv6 reservations and DHCPv6 options.
1756  {2,
1757  { OID_VARCHAR, OID_INT2 },
1758  "get_host_prefix",
1759  "SELECT h.host_id, h.dhcp_identifier, "
1760  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1761  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1762  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1763  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1764  " h.dhcp4_boot_file_name, h.auth_key, "
1765  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1766  " o.persistent, o.user_context, "
1767  " r.reservation_id, r.address, r.prefix_len, r.type, "
1768  " r.dhcp6_iaid "
1769  "FROM hosts AS h "
1770  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1771  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1772  "WHERE h.host_id = "
1773  " (SELECT host_id FROM ipv6_reservations "
1774  " WHERE address = $1 AND prefix_len = $2) "
1775  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1776  },
1777 
1778  // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR
1779  // Retrieves host information, IPv6 reservations and DHCPv6 options
1780  // associated with a host using IPv6 subnet id and prefix. This query
1781  // returns host information for a single host. However, multiple rows
1782  // are returned due to left joining IPv6 reservations and DHCPv6 options.
1783  // The number of rows returned is multiplication of number of existing
1784  // IPv6 reservations and DHCPv6 options.
1785  {2,
1786  { OID_INT8, OID_VARCHAR },
1787  "get_host_subid6_addr",
1788  "SELECT h.host_id, h.dhcp_identifier, "
1789  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1790  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1791  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1792  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1793  " h.dhcp4_boot_file_name, h.auth_key, "
1794  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1795  " o.persistent, o.user_context, "
1796  " r.reservation_id, r.address, r.prefix_len, r.type, "
1797  " r.dhcp6_iaid "
1798  "FROM hosts AS h "
1799  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1800  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1801  "WHERE h.dhcp6_subnet_id = $1 AND r.address = $2 "
1802  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1803  },
1804 
1805  // PgSqlHostDataSourceImpl::GET_HOST_SUBID4
1806  //
1807  // Retrieves host information for all hosts in a subnet, along with the
1808  // DHCPv4 options associated with it. Left joining the dhcp4_options table
1809  // results in multiple rows being returned for the same host. The hosts are
1810  // retrieved by subnet id.
1811  {1,
1812  { OID_INT8 },
1813  "get_host_subid4",
1814  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1815  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1816  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1817  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1818  " h.dhcp4_boot_file_name, h.auth_key, "
1819  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1820  " o.persistent, o.user_context "
1821  "FROM hosts AS h "
1822  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1823  "WHERE h.dhcp4_subnet_id = $1 "
1824  "ORDER BY h.host_id, o.option_id"
1825  },
1826 
1827  // PgSqlHostDataSourceImpl::GET_HOST_SUBID6
1828  //
1829  // Retrieves host information, IPv6 reservations and DHCPv6 options
1830  // associated with all hosts using the IPv6 subnet id. This query returns
1831  // host information for many hosts. However, multiple rows are
1832  // returned due to left joining IPv6 reservations and DHCPv6 options.
1833  // The number of rows returned is multiplication of number of existing
1834  // IPv6 reservations and DHCPv6 options for each host in a subnet. There
1835  // are usually many hosts in a subnet. The amount of returned data may
1836  // be huge.
1837  {1,
1838  { OID_INT8 },
1839  "get_host_subid6",
1840  "SELECT h.host_id, h.dhcp_identifier, "
1841  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1842  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1843  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1844  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1845  " h.dhcp4_boot_file_name, h.auth_key, "
1846  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1847  " o.persistent, o.user_context, "
1848  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
1849  "FROM hosts AS h "
1850  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1851  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1852  "WHERE h.dhcp6_subnet_id = $1 "
1853  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1854  },
1855 
1856  // PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME
1857  // Retrieves host information, IPv6 reservations and both DHCPv4 and
1858  // DHCPv6 options associated with all hosts using the hostname.
1859  // The LEFT JOIN clause is used to retrieve information from 4 different
1860  // tables using a single query. Hence, this query returns multiple rows
1861  // for a single host.
1862  {1,
1863  { OID_VARCHAR },
1864  "get_host_hostname",
1865  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1866  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
1867  " h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
1868  " h.user_context, "
1869  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1870  " h.dhcp4_boot_file_name, h.auth_key, "
1871  " o4.option_id, o4.code, o4.value, o4.formatted_value, o4.space, "
1872  " o4.persistent, o4.user_context, "
1873  " o6.option_id, o6.code, o6.value, o6.formatted_value, o6.space, "
1874  " o6.persistent, o6.user_context, "
1875  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
1876  "FROM hosts AS h "
1877  "LEFT JOIN dhcp4_options AS o4 ON h.host_id = o4.host_id "
1878  "LEFT JOIN dhcp6_options AS o6 ON h.host_id = o6.host_id "
1879  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1880  "WHERE lower(h.hostname) = $1 "
1881  "ORDER BY h.host_id, o4.option_id, o6.option_id, r.reservation_id"
1882  },
1883 
1884  // PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4
1885  // Retrieves host information for all hosts with a hostname in a subnet,
1886  // along with the DHCPv4 options associated with it. Left joining
1887  // the dhcp4_options table results in multiple rows being returned for
1888  // the same host.
1889  {2,
1890  { OID_VARCHAR, OID_INT8 },
1891  "get_host_hostname_subid4",
1892  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1893  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1894  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1895  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1896  " h.dhcp4_boot_file_name, h.auth_key, "
1897  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1898  " o.persistent, o.user_context "
1899  "FROM hosts AS h "
1900  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1901  "WHERE lower(h.hostname) = $1 AND h.dhcp4_subnet_id = $2 "
1902  "ORDER BY h.host_id, o.option_id"
1903  },
1904 
1905  // PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6
1906  // Retrieves host information, IPv6 reservations and DHCPv6 options
1907  // associated with all hosts using the hostname and the IPv6 subnet id.
1908  // This query returns host information for many hosts. However, multiple
1909  // rows are returned due to left joining IPv6 reservations and DHCPv6
1910  // options. The number of rows returned is multiplication of number of
1911  // existing IPv6 reservations and DHCPv6 options for each host in a subnet.
1912  {2,
1913  { OID_VARCHAR, OID_INT8 },
1914  "get_host_hostname_subid6",
1915  "SELECT h.host_id, h.dhcp_identifier, "
1916  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
1917  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1918  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1919  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1920  " h.dhcp4_boot_file_name, h.auth_key, "
1921  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1922  " o.persistent, o.user_context, "
1923  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
1924  "FROM hosts AS h "
1925  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1926  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1927  "WHERE lower(h.hostname) = $1 AND h.dhcp6_subnet_id = $2 "
1928  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1929  },
1930 
1931  // PgSqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE
1932  // Retrieves host information along with the DHCPv4 options associated with
1933  // it. Left joining the dhcp4_options table results in multiple rows being
1934  // returned for the same host. The hosts are retrieved by subnet id,
1935  // starting from specified host id. Specified number of hosts is returned.
1936  {3,
1937  { OID_INT8, OID_INT8, OID_INT8 },
1938  "get_host_subid4_page",
1939  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1940  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1941  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1942  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1943  " h.dhcp4_boot_file_name, h.auth_key, "
1944  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1945  " o.persistent, o.user_context "
1946  "FROM ( SELECT * FROM hosts AS h "
1947  " WHERE h.dhcp4_subnet_id = $1 AND h.host_id > $2 "
1948  " ORDER BY h.host_id "
1949  " LIMIT $3 ) AS h "
1950  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
1951  "ORDER BY h.host_id, o.option_id"
1952  },
1953 
1954  // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE
1955  // Retrieves host information, IPv6 reservations and DHCPv6 options
1956  // associated with a host using IPv6 subnet id. This query returns
1957  // host information for a single host. However, multiple rows are
1958  // returned due to left joining IPv6 reservations and DHCPv6 options.
1959  // The number of rows returned is multiplication of number of existing
1960  // IPv6 reservations and DHCPv6 options.
1961  {3,
1962  { OID_INT8, OID_INT8, OID_INT8 },
1963  "get_host_subid6_page",
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.user_context, "
1972  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
1973  "FROM ( SELECT * FROM hosts AS h "
1974  " WHERE h.dhcp6_subnet_id = $1 AND h.host_id > $2 "
1975  " ORDER BY h.host_id "
1976  " LIMIT $3 ) AS h "
1977  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
1978  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
1979  "ORDER BY h.host_id, o.option_id, r.reservation_id"
1980  },
1981 
1982  // PgSqlHostDataSourceImpl::GET_HOST_PAGE4
1983  // Retrieves host information along with the DHCPv4 options associated with
1984  // it. Left joining the dhcp4_options table results in multiple rows being
1985  // returned for the same host. The hosts are retrieved starting from
1986  // specified host id. Specified number of hosts is returned.
1987  {2,
1988  { OID_INT8, OID_INT8 },
1989  "get_host_page4",
1990  "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
1991  " h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
1992  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
1993  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
1994  " h.dhcp4_boot_file_name, h.auth_key, "
1995  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
1996  " o.persistent, o.user_context "
1997  "FROM ( SELECT * FROM hosts AS h "
1998  " WHERE h.host_id > $1 "
1999  " ORDER BY h.host_id "
2000  " LIMIT $2 ) AS h "
2001  "LEFT JOIN dhcp4_options AS o ON h.host_id = o.host_id "
2002  "ORDER BY h.host_id, o.option_id"
2003  },
2004 
2005  // PgSqlHostDataSourceImpl::GET_HOST_PAGE6
2006  // Retrieves host information, IPv6 reservations and DHCPv6 options
2007  // associated with a host using IPv6 subnet id. This query returns
2008  // host information for a single host. However, multiple rows are
2009  // returned due to left joining IPv6 reservations and DHCPv6 options.
2010  // The number of rows returned is multiplication of number of existing
2011  // IPv6 reservations and DHCPv6 options.
2012  {2,
2013  { OID_INT8, OID_INT8 },
2014  "get_host_page6",
2015  "SELECT h.host_id, h.dhcp_identifier, "
2016  " h.dhcp_identifier_type, h.dhcp4_subnet_id, "
2017  " h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
2018  " h.dhcp4_client_classes, h.dhcp6_client_classes, h.user_context, "
2019  " h.dhcp4_next_server, h.dhcp4_server_hostname, "
2020  " h.dhcp4_boot_file_name, h.auth_key, "
2021  " o.option_id, o.code, o.value, o.formatted_value, o.space, "
2022  " o.persistent, o.user_context, "
2023  " r.reservation_id, r.address, r.prefix_len, r.type, r.dhcp6_iaid "
2024  "FROM ( SELECT * FROM hosts AS h "
2025  " WHERE h.host_id > $1 "
2026  " ORDER BY h.host_id "
2027  " LIMIT $2 ) AS h "
2028  "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
2029  "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
2030  "ORDER BY h.host_id, o.option_id, r.reservation_id"
2031  },
2032 
2033  // PgSqlHostDataSourceImpl::INSERT_HOST_NON_UNIQUE_IP
2034  // Inserts a host into the 'hosts' table without checking that there is
2035  // a reservation for the IP address.
2036  {13,
2037  { OID_BYTEA, OID_INT2,
2040  OID_INT8, OID_VARCHAR, OID_VARCHAR, OID_VARCHAR},
2041  "insert_host_non_unique_ip",
2042  "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
2043  " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2044  " dhcp4_client_classes, dhcp6_client_classes, user_context, "
2045  " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key)"
2046  "VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 ) "
2047  "RETURNING host_id"
2048  },
2049 
2050  // PgSqlHostDataSourceImpl::INSERT_HOST_UNIQUE_IP
2051  // Inserts a host into the 'hosts' table with checking that reserved IP
2052  // address is unique. The innermost query checks if there is at least
2053  // one host for the given IP/subnet combination. For checking whether
2054  // hosts exists or not it doesn't matter if we select actual columns,
2055  // thus SELECT 1 was used as an optimization to avoid selecting data
2056  // that will be ignored anyway. If it does not exist the new host is
2057  // inserted. If the host with the given IP address already exists the
2058  // new host won't be inserted. The caller can check the number of
2059  // affected rows to detect that there was a duplicate host in the
2060  // database. Returns the inserted host id.
2061  {15,
2062  { OID_BYTEA, OID_INT2,
2066  OID_INT8, OID_INT8},
2067  "insert_host_unique_ip",
2068  "INSERT INTO hosts(dhcp_identifier, dhcp_identifier_type, "
2069  " dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
2070  " dhcp4_client_classes, dhcp6_client_classes, user_context, "
2071  " dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key)"
2072  " SELECT $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13"
2073  " WHERE NOT EXISTS ("
2074  " SELECT 1 FROM hosts WHERE ipv4_address = $14 AND dhcp4_subnet_id = $15"
2075  " LIMIT 1"
2076  " ) "
2077  "RETURNING host_id"
2078  },
2079 
2080  // PgSqlHostDataSourceImpl::INSERT_V6_RESRV_NON_UNIQUE
2081  // Inserts a single IPv6 reservation into 'reservations' table without
2082  // checking that the inserted reservation is unique.
2083  {5,
2084  { OID_VARCHAR, OID_INT2, OID_INT4, OID_INT4, OID_INT4 },
2085  "insert_v6_resrv_non_unique",
2086  "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2087  " dhcp6_iaid, host_id) "
2088  "VALUES ($1, $2, $3, $4, $5)"
2089  },
2090 
2091  // PgSqlHostDataSourceImpl::INSERT_V6_RESRV_UNIQUE
2092  // Inserts a single IPv6 reservation into 'reservations' table with
2093  // checking that the inserted reservation is unique.
2094  {7,
2096  "insert_v6_resrv_unique",
2097  "INSERT INTO ipv6_reservations(address, prefix_len, type, "
2098  " dhcp6_iaid, host_id) "
2099  "SELECT $1, $2, $3, $4, $5 "
2100  " WHERE NOT EXISTS ("
2101  " SELECT 1 FROM ipv6_reservations"
2102  " WHERE address = $6 AND prefix_len = $7"
2103  " LIMIT 1"
2104  " )"
2105  },
2106 
2107  // PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION
2108  // Inserts a single DHCPv4 option into 'dhcp4_options' table.
2109  // Using fixed scope_id = 3, which associates an option with host.
2110  {7,
2113  "insert_v4_host_option",
2114  "INSERT INTO dhcp4_options(code, value, formatted_value, space, "
2115  " persistent, user_context, host_id, scope_id) "
2116  "VALUES ($1, $2, $3, $4, $5, $6, $7, 3)"
2117  },
2118 
2119  // PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION
2120  // Inserts a single DHCPv6 option into 'dhcp6_options' table.
2121  // Using fixed scope_id = 3, which associates an option with host.
2122  {7,
2125  "insert_v6_host_option",
2126  "INSERT INTO dhcp6_options(code, value, formatted_value, space, "
2127  " persistent, user_context, host_id, scope_id) "
2128  "VALUES ($1, $2, $3, $4, $5, $6, $7, 3)"
2129  },
2130 
2131  // PgSqlHostDataSourceImpl::DEL_HOST_ADDR4
2132  // Deletes a v4 host that matches (subnet-id, addr4)
2133  {2,
2134  { OID_INT8, OID_INT8 },
2135  "del_host_addr4",
2136  "DELETE FROM hosts WHERE dhcp4_subnet_id = $1 AND ipv4_address = $2"
2137  },
2138 
2139  // PgSqlHostDataSourceImpl::DEL_HOST_ADDR6
2140  // Deletes a v6 host that matches (subnet-id, addr6)
2141  {2,
2142  { OID_INT8, OID_VARCHAR },
2143  "del_host_addr6",
2144  "DELETE FROM hosts USING ipv6_reservations "
2145  " WHERE dhcp6_subnet_id = $1 AND ipv6_reservations.address = $2"
2146  },
2147 
2148  // PgSqlHostDataSourceImpl::DEL_HOST_SUBID4_ID
2149  // Deletes a v4 host that matches (subnet4-id, identifier-type, identifier)
2150  {3,
2151  { OID_INT8, OID_INT2, OID_BYTEA },
2152  "del_host_subid4_id",
2153  "DELETE FROM hosts WHERE dhcp4_subnet_id = $1 "
2154  "AND dhcp_identifier_type = $2 "
2155  "AND dhcp_identifier = $3"
2156  },
2157 
2158  // PgSqlHostDataSourceImpl::DEL_HOST_SUBID6_ID
2159  // Deletes a v6 host that matches (subnet6-id, identifier-type, identifier)
2160  {3,
2161  { OID_INT8, OID_INT2, OID_BYTEA },
2162  "del_host_subid6_id",
2163  "DELETE FROM hosts WHERE dhcp6_subnet_id = $1 "
2164  "AND dhcp_identifier_type = $2 "
2165  "AND dhcp_identifier = $3"
2166  }
2167 }
2168 };
2169 
2170 } // namespace
2171 
2172 // PgSqlHostContext Constructor
2173 
2174 PgSqlHostContext::PgSqlHostContext(const DatabaseConnection::ParameterMap& parameters,
2175  IOServiceAccessorPtr io_service_accessor,
2176  db::DbCallback db_reconnect_callback)
2177  : conn_(parameters, io_service_accessor, db_reconnect_callback),
2178  is_readonly_(true) {
2179 }
2180 
2181 // PgSqlHostContextAlloc Constructor and Destructor
2182 
2184  PgSqlHostDataSourceImpl& mgr) : ctx_(), mgr_(mgr) {
2185 
2186  if (MultiThreadingMgr::instance().getMode()) {
2187  // multi-threaded
2188  {
2189  // we need to protect the whole pool_ operation, hence extra scope {}
2190  lock_guard<mutex> lock(mgr_.pool_->mutex_);
2191  if (!mgr_.pool_->pool_.empty()) {
2192  ctx_ = mgr_.pool_->pool_.back();
2193  mgr_.pool_->pool_.pop_back();
2194  }
2195  }
2196  if (!ctx_) {
2197  ctx_ = mgr_.createContext();
2198  }
2199  } else {
2200  // single-threaded
2201  if (mgr_.pool_->pool_.empty()) {
2202  isc_throw(Unexpected, "No available PostgreSQL host context?!");
2203  }
2204  ctx_ = mgr_.pool_->pool_.back();
2205  }
2206 }
2207 
2209  if (MultiThreadingMgr::instance().getMode()) {
2210  // multi-threaded
2211  lock_guard<mutex> lock(mgr_.pool_->mutex_);
2212  mgr_.pool_->pool_.push_back(ctx_);
2213  if (ctx_->conn_.isUnusable()) {
2214  mgr_.unusable_ = true;
2215  }
2216  } else if (ctx_->conn_.isUnusable()) {
2217  mgr_.unusable_ = true;
2218  }
2219 }
2220 
2222  : parameters_(parameters), ip_reservations_unique_(true), unusable_(false),
2223  timer_name_("") {
2224 
2225  // Create unique timer name per instance.
2226  timer_name_ = "PgSqlHostMgr[";
2227  timer_name_ += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
2228  timer_name_ += "]DbReconnectTimer";
2229 
2230  // Validate the schema version first.
2231  std::pair<uint32_t, uint32_t> code_version(PG_SCHEMA_VERSION_MAJOR,
2233  std::pair<uint32_t, uint32_t> db_version = getVersion();
2234  if (code_version != db_version) {
2236  "PostgreSQL schema version mismatch: need version: "
2237  << code_version.first << "." << code_version.second
2238  << " found version: " << db_version.first << "."
2239  << db_version.second);
2240  }
2241 
2242  // Create an initial context.
2243  pool_.reset(new PgSqlHostContextPool());
2244  pool_->pool_.push_back(createContext());
2245 }
2246 
2247 // Create context.
2248 
2254 
2255  // Open the database.
2256  ctx->conn_.openDatabase();
2257 
2258  // Now prepare the SQL statements.
2259  ctx->conn_.prepareStatements(tagged_statements.begin(),
2260  tagged_statements.begin() + WRITE_STMTS_BEGIN);
2261 
2262  // Check if the backend is explicitly configured to operate with
2263  // read only access to the database.
2264  ctx->is_readonly_ = ctx->conn_.configuredReadOnly();
2265 
2266  // If we are using read-write mode for the database we also prepare
2267  // statements for INSERTS etc.
2268  if (!ctx->is_readonly_) {
2269  ctx->conn_.prepareStatements(tagged_statements.begin() + WRITE_STMTS_BEGIN,
2270  tagged_statements.end());
2271  } else {
2273  }
2274 
2275  ctx->host_ipv4_exchange_.reset(new PgSqlHostWithOptionsExchange(PgSqlHostWithOptionsExchange::DHCP4_ONLY));
2276  ctx->host_ipv6_exchange_.reset(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP6_ONLY));
2277  ctx->host_ipv46_exchange_.reset(new PgSqlHostIPv6Exchange(PgSqlHostWithOptionsExchange::DHCP4_AND_DHCP6));
2278  ctx->host_ipv6_reservation_exchange_.reset(new PgSqlIPv6ReservationExchange());
2279  ctx->host_option_exchange_.reset(new PgSqlOptionExchange());
2280 
2281  // Create ReconnectCtl for this connection.
2282  ctx->conn_.makeReconnectCtl(timer_name_);
2283 
2284  return (ctx);
2285 }
2286 
2288 }
2289 
2290 bool
2293 
2294  // Invoke application layer connection lost callback.
2295  if (!DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl)) {
2296  return (false);
2297  }
2298 
2299  bool reopened = false;
2300 
2301  const std::string timer_name = db_reconnect_ctl->timerName();
2302 
2303  // At least one connection was lost.
2304  try {
2305  CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
2306  std::list<std::string> host_db_access_list = cfg_db->getHostDbAccessStringList();
2307  for (std::string& hds : host_db_access_list) {
2308  auto parameters = DatabaseConnection::parse(hds);
2309  if (HostMgr::delBackend("postgresql", hds, true)) {
2310  HostMgr::addBackend(hds);
2311  }
2312  }
2313  reopened = true;
2314  } catch (const std::exception& ex) {
2316  .arg(ex.what());
2317  }
2318 
2319  if (reopened) {
2320  // Cancel the timer.
2321  if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2322  TimerMgr::instance()->unregisterTimer(timer_name);
2323  }
2324 
2325  // Invoke application layer connection recovered callback.
2326  if (!DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl)) {
2327  return (false);
2328  }
2329  } else {
2330  if (!db_reconnect_ctl->checkRetries()) {
2331  // We're out of retries, log it and initiate shutdown.
2333  .arg(db_reconnect_ctl->maxRetries());
2334 
2335  // Cancel the timer.
2336  if (TimerMgr::instance()->isTimerRegistered(timer_name)) {
2337  TimerMgr::instance()->unregisterTimer(timer_name);
2338  }
2339 
2340  // Invoke application layer connection failed callback.
2342  return (false);
2343  }
2344 
2346  .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
2347  .arg(db_reconnect_ctl->maxRetries())
2348  .arg(db_reconnect_ctl->retryInterval());
2349 
2350  // Start the timer.
2351  if (!TimerMgr::instance()->isTimerRegistered(timer_name)) {
2352  TimerMgr::instance()->registerTimer(timer_name,
2353  std::bind(&PgSqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl),
2354  db_reconnect_ctl->retryInterval(),
2356  }
2357  TimerMgr::instance()->setup(timer_name);
2358  }
2359 
2360  return (true);
2361 }
2362 
2363 uint64_t
2365  StatementIndex stindex,
2366  PsqlBindArrayPtr& bind_array,
2367  const bool return_last_id) {
2368  uint64_t last_id = 0;
2369  PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
2370  tagged_statements[stindex].nbparams,
2371  &bind_array->values_[0],
2372  &bind_array->lengths_[0],
2373  &bind_array->formats_[0], 0));
2374 
2375  int s = PQresultStatus(r);
2376 
2377  if (s != PGRES_COMMAND_OK) {
2378  // Failure: check for the special case of duplicate entry.
2379  if (ctx->conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
2380  isc_throw(DuplicateEntry, "Database duplicate entry error");
2381  }
2382 
2383  // Connection determines if the error is fatal or not, and
2384  // throws the appropriate exception
2385  ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
2386  }
2387 
2388  // Get the number of affected rows.
2389  char* rows_affected = PQcmdTuples(r);
2390  if (!rows_affected) {
2392  "Could not retrieve the number of affected rows.");
2393  }
2394 
2395  // If the number of rows inserted is 0 it means that the query detected
2396  // an attempt to insert duplicated data for which there is no unique
2397  // index in the database. Unique indexes are not created in the database
2398  // when it may be sometimes allowed to insert duplicated records per
2399  // server's configuration.
2400  if (rows_affected[0] == '0') {
2401  isc_throw(DuplicateEntry, "Database duplicate entry error");
2402  }
2403 
2404  if (return_last_id) {
2405  PgSqlExchange::getColumnValue(r, 0, 0, last_id);
2406  }
2407 
2408  return (last_id);
2409 }
2410 
2411 bool
2413  StatementIndex stindex,
2414  PsqlBindArrayPtr& bind_array) {
2415  PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
2416  tagged_statements[stindex].nbparams,
2417  &bind_array->values_[0],
2418  &bind_array->lengths_[0],
2419  &bind_array->formats_[0], 0));
2420 
2421  int s = PQresultStatus(r);
2422 
2423  if (s != PGRES_COMMAND_OK) {
2424  // Connection determines if the error is fatal or not, and
2425  // throws the appropriate exception
2426  ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
2427  }
2428 
2429  // Now check how many rows (hosts) were deleted. This should be either
2430  // "0" or "1".
2431  char* rows_deleted = PQcmdTuples(r);
2432  if (!rows_deleted) {
2434  "Could not retrieve the number of deleted rows.");
2435  }
2436  return (rows_deleted[0] != '0');
2437 }
2438 
2439 void
2441  const IPv6Resrv& resv,
2442  const HostID& id) {
2443  PsqlBindArrayPtr bind_array = ctx->host_ipv6_reservation_exchange_->
2444  createBindForSend(resv, id, ip_reservations_unique_);
2445 
2446  addStatement(ctx,
2448  bind_array);
2449 }
2450 
2451 void
2453  const StatementIndex& stindex,
2454  const OptionDescriptor& opt_desc,
2455  const std::string& opt_space,
2456  const Optional<SubnetID>&,
2457  const HostID& id) {
2458  PsqlBindArrayPtr bind_array = ctx->host_option_exchange_->createBindForSend(opt_desc, opt_space, id);
2459 
2460  addStatement(ctx, stindex, bind_array);
2461 }
2462 
2463 void
2465  const StatementIndex& stindex,
2466  const ConstCfgOptionPtr& options_cfg,
2467  const uint64_t host_id) {
2468  // Get option space names and vendor space names and combine them within a
2469  // single list.
2470  std::list<std::string> option_spaces = options_cfg->getOptionSpaceNames();
2471  std::list<std::string> vendor_spaces = options_cfg->getVendorIdsSpaceNames();
2472  option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
2473  vendor_spaces.end());
2474 
2475  // For each option space retrieve all options and insert them into the
2476  // database.
2477  for (auto space = option_spaces.begin(); space != option_spaces.end(); ++space) {
2478  OptionContainerPtr options = options_cfg->getAll(*space);
2479  if (options && !options->empty()) {
2480  for (auto opt = options->begin(); opt != options->end(); ++opt) {
2481  addOption(ctx, stindex, *opt, *space, Optional<SubnetID>(), host_id);
2482  }
2483  }
2484  }
2485 }
2486 
2487 void
2489  StatementIndex stindex,
2490  PsqlBindArrayPtr bind_array,
2491  boost::shared_ptr<PgSqlHostExchange> exchange,
2492  ConstHostCollection& result,
2493  bool single) const {
2494 
2495  exchange->clear();
2496  PgSqlResult r(PQexecPrepared(ctx->conn_, tagged_statements[stindex].name,
2497  tagged_statements[stindex].nbparams,
2498  &bind_array->values_[0],
2499  &bind_array->lengths_[0],
2500  &bind_array->formats_[0], 0));
2501 
2502  ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
2503 
2504  int rows = r.getRows();
2505  for (int row = 0; row < rows; ++row) {
2506  exchange->processRowData(result, r, row);
2507 
2508  if (single && result.size() > 1) {
2509  isc_throw(MultipleRecords, "multiple records were found in the "
2510  "database where only one was expected for query "
2511  << tagged_statements[stindex].name);
2512  }
2513  }
2514 }
2515 
2518  const SubnetID& subnet_id,
2519  const Host::IdentifierType& identifier_type,
2520  const uint8_t* identifier_begin,
2521  const size_t identifier_len,
2522  StatementIndex stindex,
2523  boost::shared_ptr<PgSqlHostExchange> exchange) const {
2524 
2525  // Set up the WHERE clause value
2526  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2527 
2528  // Add the subnet id.
2529  bind_array->add(subnet_id);
2530 
2531  // Add the Identifier type.
2532  bind_array->add(static_cast<uint8_t>(identifier_type));
2533 
2534  // Add the identifier value.
2535  bind_array->add(identifier_begin, identifier_len);
2536 
2537  ConstHostCollection collection;
2538  getHostCollection(ctx, stindex, bind_array, exchange, collection, true);
2539 
2540  // Return single record if present, else clear the host.
2541  ConstHostPtr result;
2542  if (!collection.empty()) {
2543  result = *collection.begin();
2544  }
2545 
2546  return (result);
2547 }
2548 
2549 std::pair<uint32_t, uint32_t>
2554 }
2555 
2556 void
2558  if (ctx->is_readonly_) {
2559  isc_throw(ReadOnlyDb, "PostgreSQL host database backend is configured"
2560  " to operate in read only mode");
2561  }
2562 }
2563 
2564 /*********** PgSqlHostDataSource *********************/
2565 
2567  : impl_(new PgSqlHostDataSourceImpl(parameters)) {
2568 }
2569 
2571 }
2572 
2575  return impl_->parameters_;
2576 }
2577 
2578 void
2580  // Get a context
2581  PgSqlHostContextAlloc get_context(*impl_);
2582  PgSqlHostContextPtr ctx = get_context.ctx_;
2583 
2584  // If operating in read-only mode, throw exception.
2585  impl_->checkReadOnly(ctx);
2586 
2587  // Initiate PostgreSQL transaction as we will have to make multiple queries
2588  // to insert host information into multiple tables. If that fails on
2589  // any stage, the transaction will be rolled back by the destructor of
2590  // the PgSqlTransaction class.
2591  PgSqlTransaction transaction(ctx->conn_);
2592 
2593  // If we're configured to check that an IP reservation within a given subnet
2594  // is unique, the IP reservation exists and the subnet is actually set
2595  // we will be using a special query that checks for uniqueness. Otherwise,
2596  // we will use a regular insert statement.
2597  bool unique_ip = impl_->ip_reservations_unique_ && !host->getIPv4Reservation().isV4Zero()
2598  && host->getIPv4SubnetID() != SUBNET_ID_UNUSED;
2599 
2600  // Create the PgSQL Bind array for the host
2601  PsqlBindArrayPtr bind_array = ctx->host_ipv4_exchange_->createBindForSend(host, unique_ip);
2602 
2603  // ... and insert the host.
2604  uint32_t host_id = impl_->addStatement(ctx,
2607  bind_array, true);
2608 
2609  // Insert DHCPv4 options.
2610  ConstCfgOptionPtr cfg_option4 = host->getCfgOption4();
2611  if (cfg_option4) {
2612  impl_->addOptions(ctx, PgSqlHostDataSourceImpl::INSERT_V4_HOST_OPTION,
2613  cfg_option4, host_id);
2614  }
2615 
2616  // Insert DHCPv6 options.
2617  ConstCfgOptionPtr cfg_option6 = host->getCfgOption6();
2618  if (cfg_option6) {
2619  impl_->addOptions(ctx, PgSqlHostDataSourceImpl::INSERT_V6_HOST_OPTION,
2620  cfg_option6, host_id);
2621  }
2622 
2623  // Insert IPv6 reservations.
2624  IPv6ResrvRange v6resv = host->getIPv6Reservations();
2625  if (std::distance(v6resv.first, v6resv.second) > 0) {
2626  for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
2627  ++resv) {
2628  impl_->addResv(ctx, resv->second, host_id);
2629  }
2630  }
2631 
2632  // Everything went fine, so explicitly commit the transaction.
2633  transaction.commit();
2634 }
2635 
2636 bool
2638  const asiolink::IOAddress& addr) {
2639  // Get a context
2640  PgSqlHostContextAlloc get_context(*impl_);
2641  PgSqlHostContextPtr ctx = get_context.ctx_;
2642 
2643  // If operating in read-only mode, throw exception.
2644  impl_->checkReadOnly(ctx);
2645 
2646  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2647  bind_array->add(subnet_id);
2648 
2649  // v4
2650  if (addr.isV4()) {
2651  bind_array->add(addr);
2652  return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_ADDR4,
2653  bind_array));
2654  }
2655 
2656  // v6
2657  bind_array->add(addr.toText());
2658 
2659  return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_ADDR6,
2660  bind_array));
2661 }
2662 
2663 bool
2665  const Host::IdentifierType& identifier_type,
2666  const uint8_t* identifier_begin,
2667  const size_t identifier_len) {
2668  // Get a context
2669  PgSqlHostContextAlloc get_context(*impl_);
2670  PgSqlHostContextPtr ctx = get_context.ctx_;
2671 
2672  // If operating in read-only mode, throw exception.
2673  impl_->checkReadOnly(ctx);
2674 
2675  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2676 
2677  // Subnet-id
2678  bind_array->add(subnet_id);
2679 
2680  // identifier-type
2681  bind_array->add(static_cast<uint8_t>(identifier_type));
2682 
2683  // identifier
2684  bind_array->add(identifier_begin, identifier_len);
2685 
2686  return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_SUBID4_ID,
2687  bind_array));
2688 }
2689 
2690 bool
2692  const Host::IdentifierType& identifier_type,
2693  const uint8_t* identifier_begin,
2694  const size_t identifier_len) {
2695  // Get a context
2696  PgSqlHostContextAlloc get_context(*impl_);
2697  PgSqlHostContextPtr ctx = get_context.ctx_;
2698 
2699  // If operating in read-only mode, throw exception.
2700  impl_->checkReadOnly(ctx);
2701 
2702  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2703 
2704  // Subnet-id
2705  bind_array->add(subnet_id);
2706 
2707  // identifier-type
2708  bind_array->add(static_cast<uint8_t>(identifier_type));
2709 
2710  // identifier
2711  bind_array->add(identifier_begin, identifier_len);
2712 
2713  return (impl_->delStatement(ctx, PgSqlHostDataSourceImpl::DEL_HOST_SUBID6_ID,
2714  bind_array));
2715 }
2716 
2719  const uint8_t* identifier_begin,
2720  const size_t identifier_len) const {
2721  // Get a context
2722  PgSqlHostContextAlloc get_context(*impl_);
2723  PgSqlHostContextPtr ctx = get_context.ctx_;
2724 
2725  // Set up the WHERE clause value
2726  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2727 
2728  // Identifier value.
2729  bind_array->add(identifier_begin, identifier_len);
2730 
2731  // Identifier type.
2732  bind_array->add(static_cast<uint8_t>(identifier_type));
2733 
2734  ConstHostCollection result;
2735  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_DHCPID,
2736  bind_array, ctx->host_ipv46_exchange_, result, false);
2737 
2738  return (result);
2739 }
2740 
2742 PgSqlHostDataSource::getAll4(const SubnetID& subnet_id) const {
2743  // Get a context
2744  PgSqlHostContextAlloc get_context(*impl_);
2745  PgSqlHostContextPtr ctx = get_context.ctx_;
2746 
2747  // Set up the WHERE clause value
2748  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2749 
2750  // Add the subnet id.
2751  bind_array->add(subnet_id);
2752 
2753  ConstHostCollection result;
2754  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID4,
2755  bind_array, ctx->host_ipv4_exchange_, result, false);
2756 
2757  return (result);
2758 }
2759 
2761 PgSqlHostDataSource::getAll6(const SubnetID& subnet_id) const {
2762  // Get a context
2763  PgSqlHostContextAlloc get_context(*impl_);
2764  PgSqlHostContextPtr ctx = get_context.ctx_;
2765 
2766  // Set up the WHERE clause value
2767  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2768 
2769  // Add the subnet id.
2770  bind_array->add(subnet_id);
2771 
2772  ConstHostCollection result;
2773  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6,
2774  bind_array, ctx->host_ipv6_exchange_, result, false);
2775 
2776  return (result);
2777 }
2778 
2780 PgSqlHostDataSource::getAllbyHostname(const std::string& hostname) const {
2781  // Get a context
2782  PgSqlHostContextAlloc get_context(*impl_);
2783  PgSqlHostContextPtr ctx = get_context.ctx_;
2784 
2785  // Set up the WHERE clause value
2786  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2787 
2788  // Add the hostname.
2789  bind_array->add(hostname);
2790 
2791  ConstHostCollection result;
2792  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME,
2793  bind_array, ctx->host_ipv46_exchange_, result, false);
2794 
2795  return (result);
2796 }
2797 
2799 PgSqlHostDataSource::getAllbyHostname4(const std::string& hostname,
2800  const SubnetID& subnet_id) const {
2801  // Get a context
2802  PgSqlHostContextAlloc get_context(*impl_);
2803  PgSqlHostContextPtr ctx = get_context.ctx_;
2804 
2805  // Set up the WHERE clause value
2806  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2807 
2808  // Add the hostname.
2809  bind_array->add(hostname);
2810 
2811  // Add the subnet id.
2812  bind_array->add(subnet_id);
2813 
2814  ConstHostCollection result;
2815  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID4,
2816  bind_array, ctx->host_ipv4_exchange_, result, false);
2817 
2818  return (result);
2819 }
2820 
2822 PgSqlHostDataSource::getAllbyHostname6(const std::string& hostname,
2823  const SubnetID& subnet_id) const {
2824  // Get a context
2825  PgSqlHostContextAlloc get_context(*impl_);
2826  PgSqlHostContextPtr ctx = get_context.ctx_;
2827 
2828  // Set up the WHERE clause value
2829  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2830 
2831  // Add the hostname.
2832  bind_array->add(hostname);
2833 
2834  // Add the subnet id.
2835  bind_array->add(subnet_id);
2836 
2837  ConstHostCollection result;
2838  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_HOSTNAME_SUBID6,
2839  bind_array, ctx->host_ipv6_exchange_, result, false);
2840 
2841  return (result);
2842 }
2843 
2846  size_t& /*source_index*/,
2847  uint64_t lower_host_id,
2848  const HostPageSize& page_size) const {
2849  // Get a context
2850  PgSqlHostContextAlloc get_context(*impl_);
2851  PgSqlHostContextPtr ctx = get_context.ctx_;
2852 
2853  // Set up the WHERE clause value
2854  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2855 
2856  // Add the subnet id.
2857  bind_array->add(subnet_id);
2858 
2859  // Add the lower bound host id.
2860  bind_array->add(lower_host_id);
2861 
2862  // Add the page size value.
2863  string page_size_data =
2864  boost::lexical_cast<std::string>(page_size.page_size_);
2865  bind_array->add(page_size_data);
2866 
2867  ConstHostCollection result;
2868  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID4_PAGE,
2869  bind_array, ctx->host_ipv4_exchange_, result, false);
2870 
2871  return (result);
2872 }
2873 
2876  size_t& /*source_index*/,
2877  uint64_t lower_host_id,
2878  const HostPageSize& page_size) const {
2879  // Get a context
2880  PgSqlHostContextAlloc get_context(*impl_);
2881  PgSqlHostContextPtr ctx = get_context.ctx_;
2882 
2883  // Set up the WHERE clause value
2884  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2885 
2886  // Add the subnet id.
2887  bind_array->add(subnet_id);
2888 
2889  // Add the lower bound host id.
2890  bind_array->add(lower_host_id);
2891 
2892  // Add the page size value.
2893  string page_size_data =
2894  boost::lexical_cast<std::string>(page_size.page_size_);
2895  bind_array->add(page_size_data);
2896 
2897  ConstHostCollection result;
2898  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_PAGE,
2899  bind_array, ctx->host_ipv6_exchange_, result, false);
2900 
2901  return (result);
2902 }
2903 
2905 PgSqlHostDataSource::getPage4(size_t& /*source_index*/,
2906  uint64_t lower_host_id,
2907  const HostPageSize& page_size) const {
2908  // Get a context
2909  PgSqlHostContextAlloc get_context(*impl_);
2910  PgSqlHostContextPtr ctx = get_context.ctx_;
2911 
2912  // Set up the WHERE clause value
2913  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2914 
2915  // Add the lower bound host id.
2916  bind_array->add(lower_host_id);
2917 
2918  // Add the page size value.
2919  string page_size_data =
2920  boost::lexical_cast<std::string>(page_size.page_size_);
2921  bind_array->add(page_size_data);
2922 
2923  ConstHostCollection result;
2924  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_PAGE4,
2925  bind_array, ctx->host_ipv4_exchange_, result, false);
2926 
2927  return (result);
2928 }
2929 
2931 PgSqlHostDataSource::getPage6(size_t& /*source_index*/,
2932  uint64_t lower_host_id,
2933  const HostPageSize& page_size) const {
2934  // Get a context
2935  PgSqlHostContextAlloc get_context(*impl_);
2936  PgSqlHostContextPtr ctx = get_context.ctx_;
2937 
2938  // Set up the WHERE clause value
2939  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2940 
2941  // Add the lower bound host id.
2942  bind_array->add(lower_host_id);
2943 
2944  // Add the page size value.
2945  string page_size_data =
2946  boost::lexical_cast<std::string>(page_size.page_size_);
2947  bind_array->add(page_size_data);
2948 
2949  ConstHostCollection result;
2950  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_PAGE6,
2951  bind_array, ctx->host_ipv6_exchange_, result, false);
2952 
2953  return (result);
2954 }
2955 
2958  // Get a context
2959  PgSqlHostContextAlloc get_context(*impl_);
2960  PgSqlHostContextPtr ctx = get_context.ctx_;
2961 
2962  // Set up the WHERE clause value
2963  PsqlBindArrayPtr bind_array(new PsqlBindArray());
2964 
2965  // v4 Reservation address
2966  bind_array->add(address);
2967 
2968  ConstHostCollection result;
2969  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_ADDR,
2970  bind_array, ctx->host_ipv4_exchange_, result, false);
2971 
2972  return (result);
2973 }
2974 
2977  const Host::IdentifierType& identifier_type,
2978  const uint8_t* identifier_begin,
2979  const size_t identifier_len) const {
2980  // Get a context
2981  PgSqlHostContextAlloc get_context(*impl_);
2982  PgSqlHostContextPtr ctx = get_context.ctx_;
2983 
2984  return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
2986  ctx->host_ipv4_exchange_));
2987 }
2988 
2991  const asiolink::IOAddress& address) const {
2992  // Get a context
2993  PgSqlHostContextAlloc get_context(*impl_);
2994  PgSqlHostContextPtr ctx = get_context.ctx_;
2995 
2996  if (!address.isV4()) {
2997  isc_throw(BadValue, "PgSqlHostDataSource::get4(id, address) - "
2998  " wrong address type, address supplied is an IPv6 address");
2999  }
3000 
3001  // Set up the WHERE clause value
3002  PsqlBindArrayPtr bind_array(new PsqlBindArray());
3003 
3004  // Add the subnet id
3005  bind_array->add(subnet_id);
3006 
3007  // Add the address
3008  bind_array->add(address);
3009 
3010  ConstHostCollection collection;
3011  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
3012  bind_array, ctx->host_ipv4_exchange_, collection, true);
3013 
3014  // Return single record if present, else clear the host.
3015  ConstHostPtr result;
3016  if (!collection.empty()) {
3017  result = *collection.begin();
3018  }
3019 
3020  return (result);
3021 }
3022 
3025  const asiolink::IOAddress& address) const {
3026  // Get a context
3027  PgSqlHostContextAlloc get_context(*impl_);
3028  PgSqlHostContextPtr ctx = get_context.ctx_;
3029 
3030  if (!address.isV4()) {
3031  isc_throw(BadValue, "PgSqlHostDataSource::get4(id, address) - "
3032  " wrong address type, address supplied is an IPv6 address");
3033  }
3034 
3035  // Set up the WHERE clause value
3036  PsqlBindArrayPtr bind_array(new PsqlBindArray());
3037 
3038  // Add the subnet id
3039  bind_array->add(subnet_id);
3040 
3041  // Add the address
3042  bind_array->add(address);
3043 
3044  ConstHostCollection collection;
3045  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
3046  bind_array, ctx->host_ipv4_exchange_, collection, false);
3047  return (collection);
3048 }
3049 
3052  const Host::IdentifierType& identifier_type,
3053  const uint8_t* identifier_begin,
3054  const size_t identifier_len) const {
3055  // Get a context
3056  PgSqlHostContextAlloc get_context(*impl_);
3057  PgSqlHostContextPtr ctx = get_context.ctx_;
3058 
3059  return (impl_->getHost(ctx, subnet_id, identifier_type, identifier_begin, identifier_len,
3061  ctx->host_ipv6_exchange_));
3062 }
3063 
3066  const uint8_t prefix_len) const {
3067  if (!prefix.isV6()) {
3068  isc_throw(BadValue, "PgSqlHostDataSource::get6(prefix, prefix_len): "
3069  "wrong address type, address supplied is an IPv4 address");
3070  }
3071 
3072  // Get a context
3073  PgSqlHostContextAlloc get_context(*impl_);
3074  PgSqlHostContextPtr ctx = get_context.ctx_;
3075 
3076  // Set up the WHERE clause value
3077  PsqlBindArrayPtr bind_array(new PsqlBindArray());
3078 
3079  // Add the prefix
3080  bind_array->add(prefix);
3081 
3082  // Add the prefix length
3083  bind_array->add(prefix_len);
3084 
3085  ConstHostCollection collection;
3086  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_PREFIX,
3087  bind_array, ctx->host_ipv6_exchange_, collection, true);
3088 
3089  // Return single record if present, else clear the host.
3090  ConstHostPtr result;
3091  if (!collection.empty()) {
3092  result = *collection.begin();
3093  }
3094 
3095  return (result);
3096 }
3097 
3100  const asiolink::IOAddress& address) const {
3101  if (!address.isV6()) {
3102  isc_throw(BadValue, "PgSqlHostDataSource::get6(id, address): "
3103  "wrong address type, address supplied is an IPv4 address");
3104  }
3105 
3106  // Get a context
3107  PgSqlHostContextAlloc get_context(*impl_);
3108  PgSqlHostContextPtr ctx = get_context.ctx_;
3109 
3110  // Set up the WHERE clause value
3111  PsqlBindArrayPtr bind_array(new PsqlBindArray());
3112 
3113  // Add the subnet id
3114  bind_array->add(subnet_id);
3115 
3116  // Add the prefix
3117  bind_array->add(address);
3118 
3119  ConstHostCollection collection;
3120  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
3121  bind_array, ctx->host_ipv6_exchange_, collection, true);
3122 
3123  // Return single record if present, else clear the host.
3124  ConstHostPtr result;
3125  if (!collection.empty()) {
3126  result = *collection.begin();
3127  }
3128 
3129  return (result);
3130 }
3131 
3134  const asiolink::IOAddress& address) const {
3135  if (!address.isV6()) {
3136  isc_throw(BadValue, "PgSqlHostDataSource::get6(id, address): "
3137  "wrong address type, address supplied is an IPv4 address");
3138  }
3139 
3140  // Get a context
3141  PgSqlHostContextAlloc get_context(*impl_);
3142  PgSqlHostContextPtr ctx = get_context.ctx_;
3143 
3144  // Set up the WHERE clause value
3145  PsqlBindArrayPtr bind_array(new PsqlBindArray());
3146 
3147  // Add the subnet id
3148  bind_array->add(subnet_id);
3149 
3150  // Add the prefix
3151  bind_array->add(address);
3152 
3153  ConstHostCollection collection;
3154  impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
3155  bind_array, ctx->host_ipv6_exchange_, collection, false);
3156  return (collection);
3157 }
3158 
3159 
3160 // Miscellaneous database methods.
3161 
3162 std::string
3164  std::string name = "";
3165  // Get a context
3166  PgSqlHostContextAlloc get_context(*impl_);
3167  PgSqlHostContextPtr ctx = get_context.ctx_;
3168 
3169  try {
3170  name = ctx->conn_.getParameter("name");
3171  } catch (...) {
3172  // Return an empty name
3173  }
3174  return (name);
3175 }
3176 
3177 std::string
3179  return (std::string("Host data source that stores host information"
3180  "in PostgreSQL database"));
3181 }
3182 
3183 std::pair<uint32_t, uint32_t>
3185  return(impl_->getVersion());
3186 }
3187 
3188 void
3190  // Get a context
3191  PgSqlHostContextAlloc get_context(*impl_);
3192  PgSqlHostContextPtr ctx = get_context.ctx_;
3193 
3194  // If operating in read-only mode, throw exception.
3195  impl_->checkReadOnly(ctx);
3196  ctx->conn_.commit();
3197 }
3198 
3199 void
3201  // Get a context
3202  PgSqlHostContextAlloc get_context(*impl_);
3203  PgSqlHostContextPtr ctx = get_context.ctx_;
3204 
3205  // If operating in read-only mode, throw exception.
3206  impl_->checkReadOnly(ctx);
3207  ctx->conn_.rollback();
3208 }
3209 
3210 bool
3212  impl_->ip_reservations_unique_ = unique;
3213  return (true);
3214 }
3215 
3216 bool
3218  return (impl_->unusable_);
3219 }
3220 
3221 } // namespace dhcp
3222 } // namespace isc
std::string timer_name_
Timer name used to register database reconnect timer.
RAII class creating a critical section.
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.
RAII wrapper for PostgreSQL Result sets.
Option descriptor.
Definition: cfg_option.h:42
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_RECONNECT_ATTEMPT_FAILED
virtual ConstHostCollection getAllbyHostname4(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv4 subnet.
const size_t OID_BOOL
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...
Wraps value holding size of the page with host reservations.
const size_t OID_INT2
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:706
void commit()
Commits transaction.
virtual void add(const HostPtr &host)
Adds a new host to the collection.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
const size_t OID_INT8
std::pair< uint32_t, uint32_t > getVersion() const
Returns PostgreSQL schema version of the open database.
void addResv(PgSqlHostContextPtr &ctx, const IPv6Resrv &resv, const HostID &id)
Inserts IPv6 Reservation into ipv6_reservation table.
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:43
virtual ConstHostCollection getAll4(const SubnetID &subnet_id) const
Return all hosts in a DHCPv4 subnet.
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition: host.h:785
const asiolink::IOAddress & getPrefix() const
Returns prefix for the reservation.
Definition: host.h:190
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:709
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...
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition: cfgmgr.cc:25
const size_t page_size_
Holds page size.
static bool dbReconnect(ReconnectCtlPtr db_reconnect_ctl)
Attempts to reconnect the server to the host DB backend manager.
static isc::asiolink::IOServicePtr & getIOService()
Returns pointer to the IO service.
Definition: host_mgr.h:643
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_RECONNECT_FAILED
static const char DUPLICATE_KEY[]
Define the PgSql error state for a duplicate key error.
PgSqlConnection conn_
PostgreSQL connection.
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:82
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_GET_VERSION
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition: cfgmgr.cc:161
STL namespace.
virtual bool setIPReservationsUnique(const bool unique)
Controls whether IP reservations are unique or non-unique.
PgSqlHostDataSource(const db::DatabaseConnection::ParameterMap &parameters)
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)
Base class for marshalling data to and from PostgreSQL.
static ParameterMap parse(const std::string &dbaccess)
Parse database access string.
IPv6 reservation for a host.
Definition: host.h:161
int getRows() const
Returns the number of rows in the result set.
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 ...
virtual ConstHostCollection getAll6(const SubnetID &subnet_id) const
Return all hosts in a DHCPv6 subnet.
const size_t OID_TEXT
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
Exception thrown on failure to open database.
PgSqlHostDataSourceImpl(const DatabaseConnection::ParameterMap &parameters)
Constructor.
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.
Multiple lease records found where one expected.
Definition: db_exceptions.h:28
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void addOptions(PgSqlHostContextPtr &ctx, const StatementIndex &stindex, const ConstCfgOptionPtr &options_cfg, const uint64_t host_id)
Inserts multiple options into the database.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
std::string formatted_value_
Option value in textual (CSV) format.
Definition: cfg_option.h:66
Definition: edns.h:19
std::pair< IPv6ResrvIterator, IPv6ResrvIterator > IPv6ResrvRange
Definition: host.h:243
const uint32_t PG_SCHEMA_VERSION_MINOR
boost::shared_ptr< PsqlBindArray > PsqlBindArrayPtr
Defines a smart pointer to PsqlBindArray.
static bool delBackend(const std::string &db_type)
Delete an alternate host backend (aka host data source).
Definition: host_mgr.cc:53
const int DHCPSRV_DBG_TRACE_DETAIL
Additional information.
Definition: dhcpsrv_log.h:38
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.
boost::shared_ptr< CfgDbAccess > CfgDbAccessPtr
A pointer to the CfgDbAccess.
std::vector< ConstHostPtr > ConstHostCollection
Collection of the const Host objects.
Definition: host.h:791
void checkReadOnly(PgSqlHostContextPtr &ctx) const
Throws exception if database is read only.
virtual std::string getName() const
Returns the name of the open database.
bool persistent_
Persistence flag.
Definition: cfg_option.h:51
virtual ConstHostCollection getAllbyHostname(const std::string &hostname) const
Return all hosts with a hostname.
A generic exception that is thrown when an unexpected error condition occurs.
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 std::pair< uint32_t, uint32_t > getVersion() const
Returns backend version.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition: host.h:788
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)
static void getColumnValue(const PgSqlResult &r, const int row, const size_t col, std::string &value)
Fetches text column value as a string.
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.
data::ConstElementPtr getContext() const
Returns const pointer to the user context.
Definition: user_context.h:24
Common PgSql Connector Pool.
virtual ~PgSqlHostDataSource()
Virtual destructor.
bool ip_reservations_unique_
Holds the setting whether the IP reservations must be unique or may be non-unique.
Represents a device with IPv4 and/or IPv6 reservations.
Definition: host.h:297
Type
Type of the reservation.
Definition: host.h:167
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.
boost::shared_ptr< PgSqlHostContext > PgSqlHostContextPtr
Type of pointers to contexts.
IPv6ResrvCollection::const_iterator IPv6ResrvIterator
Definition: host.h:241
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
std::vector< PgSqlHostContextPtr > pool_
The vector of available contexts.
OptionPtr option_
Option instance.
Definition: cfg_option.h:45
bool delStatement(PgSqlHostContextPtr &ctx, PgSqlHostDataSourceImpl::StatementIndex stindex, PsqlBindArrayPtr &bind)
Executes statements that delete records.
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_RECONNECT_ATTEMPT_SCHEDULE
static bool invokeDbFailedCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restore failed connectivity callback.
Authentication keys.
Definition: host.h:75
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-dhcp-ddns.
std::function< bool(ReconnectCtlPtr db_reconnect_ctl)> DbCallback
Defines a callback prototype for propagating events upward.
virtual std::string getDescription() const
Returns description of the backend.
PgSqlHostContextPoolPtr pool_
The pool of contexts.
boost::shared_ptr< PgSqlHostContextPool > PgSqlHostContextPoolPtr
Type of pointers to context pools.
static bool invokeDbRecoveredCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's restored connectivity callback.
Implementation of the PgSqlHostDataSource.
const size_t OID_BYTEA
bool is_readonly_
Indicates if the database is opened in read only mode.
PgSqlHostContextAlloc(PgSqlHostDataSourceImpl &mgr)
Constructor.
virtual bool isUnusable()
Flag which indicates if the host manager has at least one unusable connection.
void addReservation(const IPv6Resrv &reservation)
Adds new IPv6 reservation.
Definition: host.cc:380
Type getType() const
Returns reservation type.
Definition: host.h:204
static bool invokeDbLostCallback(const ReconnectCtlPtr &db_reconnect_ctl)
Invokes the connection's lost connectivity callback.
#define DHCP6_OPTION_SPACE
virtual void rollback()
Rollback Transactions.
const size_t OID_INT4
#define DHCP4_OPTION_SPACE
global std option spaces
static void addBackend(const std::string &access)
Add an alternate host backend (aka host data source).
Definition: host_mgr.cc:48
boost::shared_ptr< OptionContainer > OptionContainerPtr
Pointer to the OptionContainer object.
Definition: cfg_option.h:272
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
virtual isc::db::DatabaseConnection::ParameterMap getParameters() const
Return backend parameters.
Attempt to modify data in read-only database.
Definition: db_exceptions.h:56
DatabaseConnection::ParameterMap parameters_
The parameters.
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
const size_t OID_VARCHAR
isc::log::Logger dhcpsrv_logger("dhcpsrv")
DHCP server library Logger.
Definition: dhcpsrv_log.h:56
static std::pair< uint32_t, uint32_t > getVersion(const ParameterMap &parameters)
Get the schema version.
virtual void commit()
Commit Transactions.
PgSqlHostContextPtr createContext() const
Create a new context.
IdentifierType
Type of the host identifier.
Definition: host.h:307
std::function< isc::asiolink::IOServicePtr()> IOServiceAccessor
Function which returns the IOService that can be used to recover the connection.
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
uint8_t getPrefixLen() const
Returns prefix length.
Definition: host.h:195
static const TimerMgrPtr & instance()
Returns pointer to the sole instance of the TimerMgr.
Definition: timer_mgr.cc:441
boost::shared_ptr< PgSqlHostIPv6Exchange > host_ipv6_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts, DHCPv6 options and IPv6 reservations.
boost::shared_ptr< PgSqlHostIPv6Exchange > host_ipv46_exchange_
Pointer to an object representing an exchange which can be used to retrieve hosts, DHCPv4 and DHCPv6 options, and IPv6 reservations using a single query.
std::mutex mutex_
The mutex to protect pool access.
virtual ConstHostCollection getAllbyHostname6(const std::string &hostname, const SubnetID &subnet_id) const
Return all hosts with a hostname in a DHCPv6 subnet.
boost::shared_ptr< ReconnectCtl > ReconnectCtlPtr
Pointer to an instance of ReconnectCtl.
static const StatementIndex WRITE_STMTS_BEGIN
Index of first statement performing write to the database.
const uint32_t PG_SCHEMA_VERSION_MAJOR
Define PostgreSQL backend version: 6.2.
const size_t OPTION_VALUE_MAX_LEN
Maximum length of option value.
Definition: host.h:45
uint64_t HostID
HostID (used only when storing in MySQL, PostgreSQL or Cassandra)
Definition: host.h:69
A template representing an optional value.
Definition: optional.h:36
const isc::log::MessageID DHCPSRV_PGSQL_HOST_DB_READONLY
boost::shared_ptr< PgSqlIPv6ReservationExchange > host_ipv6_reservation_exchange_
Pointer to an object representing an exchange which can be used to insert new IPv6 reservation...
Exception thrown on failure to execute a database function.
boost::shared_ptr< IOServiceAccessor > IOServiceAccessorPtr
Pointer to an instance of IOServiceAccessor.
PostgreSQL Host Context Pool.
Database duplicate entry error.
Definition: db_exceptions.h:42
uint32_t SubnetID
Unique identifier for a subnet (both v4 and v6)
Definition: lease.h:24
RAII object representing a PostgreSQL transaction.
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.
boost::shared_ptr< PgSqlHostWithOptionsExchange > host_ipv4_exchange_
The exchange objects are used for transfer of data to/from the database.
bool unusable_
Indicates if there is at least one connection that can no longer be used for normal operations...