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