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