mysql_host_data_source.cc 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  1. // Copyright (C) 2015-2016 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. #include <config.h>
  7. #include <dhcpsrv/dhcpsrv_log.h>
  8. #include <dhcpsrv/mysql_host_data_source.h>
  9. #include <dhcpsrv/db_exceptions.h>
  10. #include <boost/pointer_cast.hpp>
  11. #include <boost/static_assert.hpp>
  12. #include <mysql.h>
  13. #include <mysqld_error.h>
  14. #include <stdint.h>
  15. #include <string>
  16. using namespace isc;
  17. using namespace isc::dhcp;
  18. using namespace isc::asiolink;
  19. using namespace std;
  20. namespace {
  21. /// @brief Maximum size of an IPv6 address represented as a text string.
  22. ///
  23. /// This is 32 hexadecimal characters written in 8 groups of four, plus seven
  24. /// colon separators.
  25. const size_t ADDRESS6_TEXT_MAX_LEN = 39;
  26. /// @brief Maximum length of classes stored in a dhcp4/6_client_classes
  27. /// columns.
  28. const size_t CLIENT_CLASSES_MAX_LEN = 255;
  29. /// @brief Maximum length of the hostname stored in DNS.
  30. ///
  31. /// This length is restricted by the length of the domain-name carried
  32. /// in the Client FQDN %Option (see RFC4702 and RFC4704).
  33. const size_t HOSTNAME_MAX_LEN = 255;
  34. /// @brief Prepared MySQL statements used by the backend to insert and
  35. /// retrieve hosts from the database.
  36. TaggedStatement tagged_statements[] = {
  37. // Inserts a host into the 'hosts' table.
  38. {MySqlHostDataSource::INSERT_HOST,
  39. "INSERT INTO hosts(host_id, dhcp_identifier, dhcp_identifier_type, "
  40. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  41. "dhcp4_client_classes, dhcp6_client_classes) "
  42. "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"},
  43. // Inserts a single IPv6 reservation into 'reservations' table.
  44. {MySqlHostDataSource::INSERT_V6_RESRV,
  45. "INSERT INTO ipv6_reservations(address, prefix_len, type, "
  46. "dhcp6_iaid, host_id) "
  47. "VALUES (?,?,?,?,?)"},
  48. // Retrieves host information along with IPv6 reservations associated
  49. // with this host. If the host exists in multiple subnets, all hosts
  50. // having a specified identifier will be returned from those subnets.
  51. // Because LEFT JOIN clause is used, the number of rows returned for
  52. // a single host depends on the number of reservations.
  53. {MySqlHostDataSource::GET_HOST_DHCPID,
  54. "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
  55. "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
  56. "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
  57. "r.address, r.prefix_len, r.type, r.dhcp6_iaid "
  58. "FROM hosts AS h "
  59. "LEFT JOIN ipv6_reservations AS r "
  60. "ON h.host_id = r.host_id "
  61. "WHERE dhcp_identifier = ? AND dhcp_identifier_type = ?"},
  62. // Retrieves host information by IPv4 address. This should typically
  63. // return a single host, but if we ever allow for defining subnets
  64. // with overlapping address pools, multiple hosts may be returned.
  65. {MySqlHostDataSource::GET_HOST_ADDR,
  66. "SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
  67. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  68. "dhcp4_client_classes, dhcp6_client_classes "
  69. "FROM hosts "
  70. "WHERE ipv4_address = ?"},
  71. // Retrieves host information by subnet identifier and unique
  72. // identifier of a client. This is expected to return a single host.
  73. {MySqlHostDataSource::GET_HOST_SUBID4_DHCPID,
  74. "SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
  75. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  76. "dhcp4_client_classes, dhcp6_client_classes "
  77. "FROM hosts "
  78. "WHERE dhcp4_subnet_id = ? AND dhcp_identifier_type = ? "
  79. " AND dhcp_identifier = ?"},
  80. // Retrieves host information by subnet identifier and unique
  81. // identifier of a client. This query should return information
  82. // for a single host but multiple rows are returned due to
  83. // use of LEFT JOIN clause. The number of rows returned for a single
  84. // host dpeneds on the number of IPv6 reservations existing for
  85. // this client.
  86. {MySqlHostDataSource::GET_HOST_SUBID6_DHCPID,
  87. "SELECT DISTINCT h.host_id, h.dhcp_identifier, "
  88. "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
  89. "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
  90. "h.dhcp4_client_classes, h.dhcp6_client_classes, "
  91. "r.address, r.prefix_len, r.type, r.dhcp6_iaid "
  92. "FROM hosts AS h "
  93. "LEFT JOIN ipv6_reservations AS r "
  94. "ON h.host_id = r.host_id "
  95. "WHERE dhcp6_subnet_id = ? AND dhcp_identifier_type = ? "
  96. "AND dhcp_identifier = ? "
  97. "ORDER BY h.host_id, r.prefix_len, r.address"},
  98. // Retrieves host information using subnet identifier and the
  99. // IPv4 address reservation. This should return inforamation for
  100. // a single host.
  101. {MySqlHostDataSource::GET_HOST_SUBID_ADDR,
  102. "SELECT host_id, dhcp_identifier, dhcp_identifier_type, "
  103. "dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, "
  104. "dhcp4_client_classes, dhcp6_client_classes "
  105. "FROM hosts "
  106. "WHERE dhcp4_subnet_id = ? AND ipv4_address = ?"},
  107. // Retrieves host information using IPv6 prefix and prefix length
  108. // or IPv6 address. This query returns host information for a
  109. // single host. However, multiple rows are returned by this
  110. // query due to use of LEFT JOIN clause with 'ipv6_reservations'
  111. // table. The number of rows returned depends on the number of
  112. // reservations for a particular host.
  113. {MySqlHostDataSource::GET_HOST_PREFIX,
  114. "SELECT DISTINCT h.host_id, h.dhcp_identifier, "
  115. "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
  116. "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
  117. "h.dhcp4_client_classes, h.dhcp6_client_classes, "
  118. "r.address, r.prefix_len, r.type, r.dhcp6_iaid "
  119. "FROM hosts AS h "
  120. "LEFT JOIN ipv6_reservations AS r "
  121. "ON h.host_id = r.host_id "
  122. "WHERE h.host_id = "
  123. "(SELECT host_id FROM ipv6_reservations "
  124. "WHERE address = ? AND prefix_len = ?) "
  125. "ORDER BY h.host_id, r.prefix_len, r.address"},
  126. // Retrieves MySQL schema version.
  127. {MySqlHostDataSource::GET_VERSION,
  128. "SELECT version, minor FROM schema_version"},
  129. // Marks the end of the statements table.
  130. {MySqlHostDataSource::NUM_STATEMENTS, NULL}
  131. };
  132. /// @brief This class provides mechanisms for sending and retrieving
  133. /// information from the 'hosts' table.
  134. ///
  135. /// This class should be used to create new entries in the 'hosts'
  136. /// table and to retrieve DHCPv4 reservations from this table. The
  137. /// queries used with this class do not retrieve IPv6 reservations for
  138. /// the hosts to minimize negative impact on performance.
  139. ///
  140. /// The derived class MySqlHostIPv6Exchange extends this class to facilitate
  141. /// retrieving IPv6 reservations along with the host information.
  142. class MySqlHostExchange {
  143. private:
  144. /// @brief Number of columns returned for queries used with this class.
  145. static const size_t HOST_COLUMNS = 9;
  146. public:
  147. /// @brief Constructor
  148. ///
  149. /// The initialization of the variables here is only to satisfy cppcheck -
  150. /// all variables are initialized/set in the methods before they are used.
  151. MySqlHostExchange()
  152. : bind_(HOST_COLUMNS), columns_(HOST_COLUMNS),
  153. error_(HOST_COLUMNS, MLM_FALSE), host_id_(0),
  154. dhcp_identifier_length_(0), dhcp_identifier_type_(0),
  155. dhcp4_subnet_id_(0), dhcp6_subnet_id_(0), ipv4_address_(0),
  156. hostname_length_(0), dhcp4_client_classes_length_(0),
  157. dhcp6_client_classes_length_(0), dhcp4_subnet_id_null_(MLM_FALSE),
  158. dhcp6_subnet_id_null_(MLM_FALSE), ipv4_address_null_(MLM_FALSE),
  159. hostname_null_(MLM_FALSE), dhcp4_client_classes_null_(MLM_FALSE),
  160. dhcp6_client_classes_null_(MLM_FALSE) {
  161. // Fill arrays with 0 so as they don't include any garbage.
  162. memset(dhcp_identifier_buffer_, 0, sizeof(dhcp_identifier_buffer_));
  163. memset(hostname_, 0, sizeof(hostname_));
  164. memset(dhcp4_client_classes_, 0, sizeof(dhcp4_client_classes_));
  165. memset(dhcp6_client_classes_, 0, sizeof(dhcp6_client_classes_));
  166. // Set the column names (for error messages)
  167. columns_[0] = "host_id";
  168. columns_[1] = "dhcp_identifier";
  169. columns_[2] = "dhcp_identifier_type";
  170. columns_[3] = "dhcp4_subnet_id";
  171. columns_[4] = "dhcp6_subnet_id";
  172. columns_[5] = "ipv4_address";
  173. columns_[6] = "hostname";
  174. columns_[7] = "dhcp4_client_classes";
  175. columns_[8] = "dhcp6_client_classes";
  176. BOOST_STATIC_ASSERT(8 < HOST_COLUMNS);
  177. };
  178. /// @brief Virtual destructor.
  179. virtual ~MySqlHostExchange() {
  180. }
  181. /// @brief Returns value of host id.
  182. ///
  183. /// This method is used by derived classes.
  184. uint64_t getHostId() const {
  185. return (host_id_);
  186. };
  187. /// @brief Set error indicators
  188. ///
  189. /// Sets the error indicator for each of the MYSQL_BIND elements. It points
  190. /// the "error" field within an element of the input array to the
  191. /// corresponding element of the passed error array.
  192. ///
  193. /// @param bind Array of BIND elements
  194. /// @param error Array of error elements. If there is an error in getting
  195. /// data associated with one of the "bind" elements, the
  196. /// corresponding element in the error array is set to MLM_TRUE.
  197. static void setErrorIndicators(std::vector<MYSQL_BIND>& bind,
  198. std::vector<my_bool>& error) {
  199. for (size_t i = 0; i < error.size(); ++i) {
  200. error[i] = MLM_FALSE;
  201. bind[i].error = reinterpret_cast<char*>(&error[i]);
  202. }
  203. };
  204. /// @brief Return columns in error
  205. ///
  206. /// If an error is returned from a fetch (in particular, a truncated
  207. /// status), this method can be called to get the names of the fields in
  208. /// error. It returns a string comprising the names of the fields
  209. /// separated by commas. In the case of there being no error indicators
  210. /// set, it returns the string "(None)".
  211. ///
  212. /// @param error Array of error elements. An element is set to MLM_TRUE
  213. /// if the corresponding column in the database is the source of
  214. /// the error.
  215. /// @param names Array of column names, the same size as the error array.
  216. /// @param count Size of each of the arrays.
  217. static std::string getColumnsInError(std::vector<my_bool>& error,
  218. const std::vector<std::string>& names) {
  219. std::string result = "";
  220. // Accumulate list of column names
  221. for (size_t i = 0; i < names.size(); ++i) {
  222. if (error[i] == MLM_TRUE) {
  223. if (!result.empty()) {
  224. result += ", ";
  225. }
  226. result += names[i];
  227. }
  228. }
  229. if (result.empty()) {
  230. result = "(None)";
  231. }
  232. return (result);
  233. };
  234. /// @brief Create MYSQL_BIND objects for Host Pointer
  235. ///
  236. /// Fills in the MYSQL_BIND array for sending data stored in the Host object
  237. /// to the database.
  238. ///
  239. /// @param host Host object to be added to the database.
  240. /// None of the fields in the host reservation are modified -
  241. /// the host data is only read.
  242. ///
  243. /// @return Vector of MySQL BIND objects representing the data to be added.
  244. std::vector<MYSQL_BIND> createBindForSend(const HostPtr& host) {
  245. // Store host object to ensure it remains valid.
  246. host_ = host;
  247. // Initialize prior to constructing the array of MYSQL_BIND structures.
  248. // It sets all fields, including is_null, to zero, so we need to set
  249. // is_null only if it should be true. This gives up minor performance
  250. // benefit while being safe approach.
  251. memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
  252. // Set up the structures for the various components of the host structure.
  253. try {
  254. // host_id : INT UNSIGNED NOT NULL
  255. // The host_id is auto_incremented by MySQL database,
  256. // so we need to pass the NULL value
  257. host_id_ = static_cast<uint32_t>(NULL);
  258. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  259. bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
  260. bind_[0].is_unsigned = MLM_TRUE;
  261. // dhcp_identifier : VARBINARY(128) NOT NULL
  262. // Check which of the identifiers is used and set values accordingly
  263. if (host->getDuid()) {
  264. dhcp_identifier_length_ = host->getDuid()->getDuid().size();
  265. memcpy(static_cast<void*>(dhcp_identifier_buffer_),
  266. &(host->getDuid()->getDuid()[0]),
  267. host->getDuid()->getDuid().size());
  268. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  269. bind_[1].buffer = dhcp_identifier_buffer_;
  270. bind_[1].buffer_length = dhcp_identifier_length_;
  271. bind_[1].length = &dhcp_identifier_length_;
  272. } else if (host->getHWAddress()){
  273. dhcp_identifier_length_ = host->getHWAddress()->hwaddr_.size();
  274. memcpy(static_cast<void*>(dhcp_identifier_buffer_),
  275. &(host->getHWAddress()->hwaddr_[0]),
  276. host->getHWAddress()->hwaddr_.size());
  277. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  278. bind_[1].buffer = dhcp_identifier_buffer_;
  279. bind_[1].buffer_length = dhcp_identifier_length_;
  280. bind_[1].length = &dhcp_identifier_length_;
  281. } else {
  282. isc_throw(DbOperationError, "Host object doesn't contain any"
  283. " identifier which can be used to retrieve information"
  284. " from the database about its static reservations");
  285. }
  286. // dhcp_identifier_type : TINYINT NOT NULL
  287. // Check which of the identifier types is used and set values accordingly
  288. if (host->getHWAddress()) {
  289. dhcp_identifier_type_ = BaseHostDataSource::ID_HWADDR; // 0
  290. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  291. bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
  292. bind_[2].is_unsigned = MLM_TRUE;
  293. } else if (host->getDuid()) {
  294. dhcp_identifier_type_ = BaseHostDataSource::ID_DUID; // 1
  295. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  296. bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
  297. bind_[2].is_unsigned = MLM_TRUE;
  298. }
  299. // dhcp4_subnet_id : INT UNSIGNED NULL
  300. // Can't take an address of intermediate object, so let's store it
  301. // in dhcp4_subnet_id_
  302. dhcp4_subnet_id_ = host->getIPv4SubnetID();
  303. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  304. bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
  305. bind_[3].is_unsigned = MLM_TRUE;
  306. // dhcp6_subnet_id : INT UNSIGNED NULL
  307. // Can't take an address of intermediate object, so let's store it
  308. // in dhcp6_subnet_id_
  309. dhcp6_subnet_id_ = host->getIPv6SubnetID();
  310. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  311. bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
  312. bind_[4].is_unsigned = MLM_TRUE;
  313. // ipv4_address : INT UNSIGNED NULL
  314. // The address in the Host structure is an IOAddress object. Convert
  315. // this to an integer for storage.
  316. ipv4_address_ = static_cast<uint32_t>(host->getIPv4Reservation());
  317. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  318. bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
  319. bind_[5].is_unsigned = MLM_TRUE;
  320. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  321. // reasons, see memset() above
  322. // hostname : VARCHAR(255) NULL
  323. strncpy(hostname_, host->getHostname().c_str(), HOSTNAME_MAX_LEN - 1);
  324. hostname_length_ = host->getHostname().length();
  325. bind_[6].buffer_type = MYSQL_TYPE_STRING;
  326. bind_[6].buffer = reinterpret_cast<char*>(hostname_);
  327. bind_[6].buffer_length = hostname_length_;
  328. // dhcp4_client_classes : VARCHAR(255) NULL
  329. bind_[7].buffer_type = MYSQL_TYPE_STRING;
  330. // Override default separator to not include space after comma.
  331. string classes4_txt = host->getClientClasses4().toText(",");
  332. strncpy(dhcp4_client_classes_, classes4_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
  333. bind_[7].buffer = dhcp4_client_classes_;
  334. bind_[7].buffer_length = classes4_txt.length();
  335. // dhcp6_client_classes : VARCHAR(255) NULL
  336. bind_[8].buffer_type = MYSQL_TYPE_STRING;
  337. // Override default separator to not include space after comma.
  338. string classes6_txt = host->getClientClasses6().toText(",");
  339. strncpy(dhcp6_client_classes_, classes6_txt.c_str(), CLIENT_CLASSES_MAX_LEN - 1);
  340. bind_[8].buffer = dhcp6_client_classes_;
  341. bind_[8].buffer_length = classes6_txt.length();
  342. bind_[8].buffer_length = sizeof(host->getClientClasses6());
  343. } catch (const std::exception& ex) {
  344. isc_throw(DbOperationError,
  345. "Could not create bind array from Host: "
  346. << host->getHostname() << ", reason: " << ex.what());
  347. }
  348. // Add the data to the vector. Note the end element is one after the
  349. // end of the array.
  350. return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[HOST_COLUMNS]));
  351. };
  352. /// @brief Create BIND array to receive Host data.
  353. ///
  354. /// Creates a MYSQL_BIND array to receive Host data from the database.
  355. /// After data is successfully received, @ref retrieveHost can be called
  356. /// to retrieve the Host object.
  357. ///
  358. /// @return Vector of MYSQL_BIND objects representing data to be retrieved.
  359. virtual std::vector<MYSQL_BIND> createBindForReceive() {
  360. // Initialize MYSQL_BIND array.
  361. // It sets all fields, including is_null, to zero, so we need to set
  362. // is_null only if it should be true. This gives up minor performance
  363. // benefit while being safe approach. For improved readability, the
  364. // code that explicitly sets is_null is there, but is commented out.
  365. // This also takes care of seeeting bind_[X].is_null to MLM_FALSE.
  366. memset(&bind_[0], 0, sizeof(MYSQL_BIND) * bind_.size());
  367. // host_id : INT UNSIGNED NOT NULL
  368. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  369. bind_[0].buffer = reinterpret_cast<char*>(&host_id_);
  370. bind_[0].is_unsigned = MLM_TRUE;
  371. // dhcp_identifier : VARBINARY(128) NOT NULL
  372. dhcp_identifier_length_ = sizeof(dhcp_identifier_buffer_);
  373. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  374. bind_[1].buffer = reinterpret_cast<char*>(dhcp_identifier_buffer_);
  375. bind_[1].buffer_length = dhcp_identifier_length_;
  376. bind_[1].length = &dhcp_identifier_length_;
  377. // dhcp_identifier_type : TINYINT NOT NULL
  378. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  379. bind_[2].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
  380. bind_[2].is_unsigned = MLM_TRUE;
  381. // dhcp4_subnet_id : INT UNSIGNED NULL
  382. dhcp4_subnet_id_null_ = MLM_FALSE;
  383. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  384. bind_[3].buffer = reinterpret_cast<char*>(&dhcp4_subnet_id_);
  385. bind_[3].is_unsigned = MLM_TRUE;
  386. bind_[3].is_null = &dhcp4_subnet_id_null_;
  387. // dhcp6_subnet_id : INT UNSIGNED NULL
  388. dhcp6_subnet_id_null_ = MLM_FALSE;
  389. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  390. bind_[4].buffer = reinterpret_cast<char*>(&dhcp6_subnet_id_);
  391. bind_[4].is_unsigned = MLM_TRUE;
  392. bind_[4].is_null = &dhcp6_subnet_id_null_;
  393. // ipv4_address : INT UNSIGNED NULL
  394. ipv4_address_null_ = MLM_FALSE;
  395. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  396. bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
  397. bind_[5].is_unsigned = MLM_TRUE;
  398. bind_[5].is_null = &ipv4_address_null_;
  399. // hostname : VARCHAR(255) NULL
  400. hostname_null_ = MLM_FALSE;
  401. hostname_length_ = sizeof(hostname_);
  402. bind_[6].buffer_type = MYSQL_TYPE_STRING;
  403. bind_[6].buffer = reinterpret_cast<char*>(hostname_);
  404. bind_[6].buffer_length = hostname_length_;
  405. bind_[6].length = &hostname_length_;
  406. bind_[6].is_null = &hostname_null_;
  407. // dhcp4_client_classes : VARCHAR(255) NULL
  408. dhcp4_client_classes_null_ = MLM_FALSE;
  409. dhcp4_client_classes_length_ = sizeof(dhcp4_client_classes_);
  410. bind_[7].buffer_type = MYSQL_TYPE_STRING;
  411. bind_[7].buffer = reinterpret_cast<char*>(dhcp4_client_classes_);
  412. bind_[7].buffer_length = dhcp4_client_classes_length_;
  413. bind_[7].length = &dhcp4_client_classes_length_;
  414. bind_[7].is_null = &dhcp4_client_classes_null_;
  415. // dhcp6_client_classes : VARCHAR(255) NULL
  416. dhcp6_client_classes_null_ = MLM_FALSE;
  417. dhcp6_client_classes_length_ = sizeof(dhcp6_client_classes_);
  418. bind_[8].buffer_type = MYSQL_TYPE_STRING;
  419. bind_[8].buffer = reinterpret_cast<char*>(dhcp6_client_classes_);
  420. bind_[8].buffer_length = dhcp6_client_classes_length_;
  421. bind_[8].length = &dhcp6_client_classes_length_;
  422. bind_[8].is_null = &dhcp6_client_classes_null_;
  423. // Add the error flags
  424. setErrorIndicators(bind_, error_);
  425. // Add the data to the vector. Note the end element is one after the
  426. // end of the array.
  427. return (bind_);
  428. };
  429. /// @brief Copy received data into Host object
  430. ///
  431. /// This function copies information about the host into a newly created
  432. /// @ref Host object. This method is called after @ref createBindForReceive.
  433. /// has been used.
  434. ///
  435. /// @return Host Pointer to a @ref HostPtr object holding a pointer to the
  436. /// @ref Host object returned.
  437. HostPtr retrieveHost() {
  438. // Set the dhcp identifier type in a variable of the appropriate data type,
  439. // which has been initialized with an arbitrary (but valid) value.
  440. Host::IdentifierType type = Host::IDENT_HWADDR;
  441. switch (dhcp_identifier_type_) {
  442. case 0:
  443. type = Host::IDENT_HWADDR;
  444. break;
  445. case 1:
  446. type = Host::IDENT_DUID;
  447. break;
  448. default:
  449. isc_throw(BadValue, "invalid dhcp identifier type returned: "
  450. << static_cast<int>(dhcp_identifier_type_)
  451. << ". Only 0 or 1 are supported.");
  452. }
  453. // Set DHCPv4 subnet ID to the value returned. If NULL returned, set to 0.
  454. SubnetID ipv4_subnet_id(0);
  455. if (dhcp4_subnet_id_null_ == MLM_FALSE) {
  456. ipv4_subnet_id = static_cast<SubnetID>(dhcp4_subnet_id_);
  457. }
  458. // Set DHCPv6 subnet ID to the value returned. If NULL returned, set to 0.
  459. SubnetID ipv6_subnet_id(0);
  460. if (dhcp6_subnet_id_null_ == MLM_FALSE) {
  461. ipv6_subnet_id = static_cast<SubnetID>(dhcp6_subnet_id_);
  462. }
  463. // Set IPv4 address reservation if it was given, if not, set IPv4 zero address
  464. asiolink::IOAddress ipv4_reservation = asiolink::IOAddress::IPV4_ZERO_ADDRESS();
  465. if (ipv4_address_null_ == MLM_FALSE) {
  466. ipv4_reservation = asiolink::IOAddress(ipv4_address_);
  467. }
  468. // Set hostname if non NULL value returned. Otherwise, leave an empty string.
  469. std::string hostname;
  470. if (hostname_null_ == MLM_FALSE) {
  471. hostname = std::string(hostname_, hostname_length_);
  472. }
  473. // Set DHCPv4 client classes if non NULL value returned.
  474. std::string dhcp4_client_classes;
  475. if (dhcp4_client_classes_null_ == MLM_FALSE) {
  476. dhcp4_client_classes = std::string(dhcp4_client_classes_,
  477. dhcp4_client_classes_length_);
  478. }
  479. // Set DHCPv6 client classes if non NULL value returned.
  480. std::string dhcp6_client_classes;
  481. if (dhcp6_client_classes_null_ == MLM_FALSE) {
  482. dhcp6_client_classes = std::string(dhcp6_client_classes_,
  483. dhcp6_client_classes_length_);
  484. }
  485. // Create and return Host object from the data gathered.
  486. HostPtr h(new Host(dhcp_identifier_buffer_, dhcp_identifier_length_,
  487. type, ipv4_subnet_id, ipv6_subnet_id, ipv4_reservation,
  488. hostname, dhcp4_client_classes, dhcp6_client_classes));
  489. h->setHostId(host_id_);
  490. return (h);
  491. };
  492. /// @brief Processes one row of data fetched from a database.
  493. ///
  494. /// The processed data must contain host id, which uniquely identifies a
  495. /// host. This method creates a host and inserts it to the hosts collection
  496. /// only if the last inserted host has a different host id. This prevents
  497. /// adding duplicated hosts to the collection, assuming that processed
  498. /// rows are primarily ordered by host id column.
  499. ///
  500. /// @todo This method will need to be extended to process options
  501. /// associated with hosts.
  502. ///
  503. /// @param [out] hosts Collection of hosts to which a new host created
  504. /// from the processed data should be inserted.
  505. virtual void processFetchedData(ConstHostCollection& hosts) {
  506. HostPtr host;
  507. // Add new host only if there are no hosts yet or the host id of the
  508. // most recently added host is different than the host id of the
  509. // currently processed host.
  510. if (hosts.empty() || (hosts.back()->getHostId() != getHostId())) {
  511. // Create Host object from the fetched data and append it to the
  512. // collection.
  513. host = retrieveHost();
  514. hosts.push_back(host);
  515. }
  516. }
  517. /// @brief Return columns in error
  518. ///
  519. /// If an error is returned from a fetch (in particular, a truncated
  520. /// status), this method can be called to get the names of the fields in
  521. /// error. It returns a string comprising the names of the fields
  522. /// separated by commas. In the case of there being no error indicators
  523. /// set, it returns the string "(None)".
  524. ///
  525. /// @return Comma-separated list of columns in error, or the string
  526. /// "(None)".
  527. std::string getErrorColumns() {
  528. return (getColumnsInError(error_, columns_));
  529. };
  530. protected:
  531. /// Vector of MySQL bindings.
  532. std::vector<MYSQL_BIND> bind_;
  533. /// Column names.
  534. std::vector<std::string> columns_;
  535. /// Error array.
  536. std::vector<my_bool> error_;
  537. /// Pointer to Host object holding information to be inserted into
  538. /// Hosts table.
  539. HostPtr host_;
  540. private:
  541. /// Host identifier (primary key in Hosts table).
  542. uint64_t host_id_;
  543. /// Buffer holding client's identifier (e.g. DUID, HW address)
  544. /// in the binary format.
  545. uint8_t dhcp_identifier_buffer_[DUID::MAX_DUID_LEN];
  546. /// Length of a data in the dhcp_identifier_buffer_.
  547. size_t dhcp_identifier_length_;
  548. /// Type of the identifier in the dhcp_identifier_buffer_. This
  549. /// value corresponds to the @ref Host::IdentifierType value.
  550. uint8_t dhcp_identifier_type_;
  551. /// DHCPv4 subnet identifier.
  552. uint32_t dhcp4_subnet_id_;
  553. /// DHCPv6 subnet identifier.
  554. uint32_t dhcp6_subnet_id_;
  555. /// Reserved IPv4 address.
  556. uint32_t ipv4_address_;
  557. /// Name reserved for the host.
  558. char hostname_[HOSTNAME_MAX_LEN];
  559. /// Hostname length.
  560. unsigned long hostname_length_;
  561. /// A string holding comma separated list of DHCPv4 client classes.
  562. char dhcp4_client_classes_[CLIENT_CLASSES_MAX_LEN];
  563. /// A length of the string holding comma separated list of DHCPv4
  564. /// client classes.
  565. unsigned long dhcp4_client_classes_length_;
  566. /// A string holding comma separated list of DHCPv6 client classes.
  567. char dhcp6_client_classes_[CLIENT_CLASSES_MAX_LEN];
  568. /// A length of the string holding comma separated list of DHCPv6
  569. /// client classes.
  570. unsigned long dhcp6_client_classes_length_;
  571. /// @name Boolean values indicating if values of specific columns in
  572. /// the database are NULL.
  573. //@{
  574. /// Boolean flag indicating if the value of the DHCPv4 subnet is NULL.
  575. my_bool dhcp4_subnet_id_null_;
  576. /// Boolean flag indicating if the value of the DHCPv6 subnet is NULL.
  577. my_bool dhcp6_subnet_id_null_;
  578. /// Boolean flag indicating if the value of IPv4 reservation is NULL.
  579. my_bool ipv4_address_null_;
  580. /// Boolean flag indicating if the value if hostname is NULL.
  581. my_bool hostname_null_;
  582. /// Boolean flag indicating if the value of DHCPv4 client classes is
  583. /// NULL.
  584. my_bool dhcp4_client_classes_null_;
  585. /// Boolean flag indicating if the value of DHCPv6 client classes is
  586. /// NULL.
  587. my_bool dhcp6_client_classes_null_;
  588. //@}
  589. };
  590. /// @brief This class provides mechanisms for sending and retrieving
  591. /// host information and associated IPv6 reservations.
  592. ///
  593. /// This class extends the @ref MySqlHostExchange class with the
  594. /// mechanisms to retrieve IPv6 reservations along with host
  595. /// information. It is assumed that both host data and IPv6
  596. /// reservations are retrieved with a single query (using LEFT JOIN
  597. /// MySQL clause). Because the host to IPv6 reservation is a 1-to-many
  598. /// relation, the same row from the Host table is returned many times
  599. /// (for each IPv6 reservation). This class is responsible for
  600. /// converting those multiple host instances into a single Host
  601. /// object with multiple IPv6 reservations.
  602. class MySqlHostIPv6Exchange : public MySqlHostExchange {
  603. private:
  604. /// @brief Number of columns returned in the queries used by
  605. /// @ref MySqlHostIPv6Exchange.
  606. static const size_t RESERVATION_COLUMNS = 13;
  607. public:
  608. /// @brief Constructor.
  609. ///
  610. /// Apart from initializing the base class data structures it also
  611. /// initializes values representing IPv6 reservation information.
  612. MySqlHostIPv6Exchange()
  613. : MySqlHostExchange(), reserv_type_(0), reserv_type_null_(MLM_FALSE),
  614. ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0) {
  615. memset(ipv6_address_buffer_, 0, sizeof(ipv6_address_buffer_));
  616. // Append additional columns returned by the queries.
  617. columns_.push_back("address");
  618. columns_.push_back("prefix_len");
  619. columns_.push_back("type");
  620. columns_.push_back("dhcp6_iaid");
  621. // Resize binding table initialized in the base class. Do not
  622. // run memset on this table, because it is when createBindForReceive
  623. // is called.
  624. bind_.resize(RESERVATION_COLUMNS);
  625. // Resize error table.
  626. error_.resize(RESERVATION_COLUMNS);
  627. std::fill(&error_[0], &error_[RESERVATION_COLUMNS], MLM_FALSE);
  628. }
  629. /// @brief Checks if a currently processed row contains IPv6 reservation.
  630. ///
  631. /// @return true if IPv6 reservation data is non-null for the processed
  632. /// row, false otherwise.
  633. bool hasReservation() const {
  634. return (reserv_type_null_ == MLM_FALSE);
  635. };
  636. /// @brief Create IPv6 reservation from the data contained in the
  637. /// currently processed row.
  638. ///
  639. /// Called after the MYSQL_BIND array created by createBindForReceive().
  640. ///
  641. /// @return IPv6Resrv object (containing IPv6 address or prefix reservation)
  642. IPv6Resrv retrieveReservation() {
  643. // Set the IPv6 Reservation type (0 = IA_NA, 2 = IA_PD)
  644. IPv6Resrv::Type type = IPv6Resrv::TYPE_NA;
  645. switch (reserv_type_) {
  646. case 0:
  647. type = IPv6Resrv::TYPE_NA;
  648. break;
  649. case 2:
  650. type = IPv6Resrv::TYPE_PD;
  651. break;
  652. default:
  653. isc_throw(BadValue,
  654. "invalid IPv6 reservation type returned: "
  655. << static_cast<int>(reserv_type_)
  656. << ". Only 0 or 2 are allowed.");
  657. }
  658. ipv6_address_buffer_[ipv6_address_buffer_len_] = '\0';
  659. std::string address = ipv6_address_buffer_;
  660. IPv6Resrv r(type, IOAddress(address), prefix_len_);
  661. return (r);
  662. };
  663. /// @brief Processes one row of data fetched from a database.
  664. ///
  665. /// The processed data must contain host id, which uniquely identifies a
  666. /// host. This method creates a host and inserts it to the hosts collection
  667. /// only if the last inserted host has a different host id. This prevents
  668. /// adding duplicated hosts to the collection, assuming that processed
  669. /// rows are primarily ordered by host id column.
  670. ///
  671. /// For any returned row which contains IPv6 reservation information it
  672. /// creates a @ref IPv6Resrv and appends it to the collection of the
  673. /// IPv6 reservations in a Host object.
  674. ///
  675. /// @todo This method will need to be extended to process DHCPv6 options
  676. /// associated with hosts.
  677. ///
  678. /// @param [out] hosts Collection of hosts to which a new host created
  679. /// from the processed data should be inserted.
  680. virtual void processFetchedData(ConstHostCollection& hosts) {
  681. HostPtr host;
  682. HostPtr most_recent_host;
  683. // If there are any hosts already created, let's obtain an instance
  684. // to the most recently added host. We will have to check if the
  685. // currently processed row contains some data for this host or a
  686. // different host. In the former case, we'll need to update the
  687. // host information.
  688. if (!hosts.empty()) {
  689. // Const cast is not very elegant way to deal with it, but
  690. // there is a good reason to use it here. This method is called
  691. // to build a collection of const hosts to be returned to the
  692. // caller. If we wanted to use non-const collection we'd need
  693. // to copy the whole collection before returning it, which has
  694. // performance implications. Alternatively, we could store the
  695. // most recently added host in a class member but this would
  696. // make the code less readable.
  697. most_recent_host = boost::const_pointer_cast<Host>(hosts.back());
  698. }
  699. // If there is no existing host or the new host id doesn't match
  700. // we need to create a new host.
  701. if (!most_recent_host || (most_recent_host->getHostId() != getHostId())) {
  702. host = retrieveHost();
  703. // If the row also contains IPv6 reservation we should add it
  704. // to the host.
  705. if (hasReservation()) {
  706. host->addReservation(retrieveReservation());
  707. }
  708. // In any case let's put the new host in the results.
  709. hosts.push_back(host);
  710. // If the returned row pertains to an existing host, let's just
  711. // add a reservation.
  712. } else if (hasReservation() && most_recent_host) {
  713. most_recent_host->addReservation(retrieveReservation());
  714. }
  715. }
  716. /// @brief Create BIND array to receive Host data with IPv6 reservations.
  717. ///
  718. /// Creates a MYSQL_BIND array to receive Host data from the database.
  719. /// After data is successfully received, @ref processedFetchedData is
  720. /// called for each returned row to build collection of @ref Host
  721. /// objects with associated IPv6 reservations.
  722. ///
  723. /// @return Vector of MYSQL_BIND objects representing data to be retrieved.
  724. virtual std::vector<MYSQL_BIND> createBindForReceive() {
  725. // The following call sets bind_ values between 0 and 8.
  726. static_cast<void>(MySqlHostExchange::createBindForReceive());
  727. // IPv6 address/prefix VARCHAR(39)
  728. ipv6_address_buffer_len_ = sizeof(ipv6_address_buffer_) - 1;
  729. bind_[9].buffer_type = MYSQL_TYPE_STRING;
  730. bind_[9].buffer = ipv6_address_buffer_;
  731. bind_[9].buffer_length = ipv6_address_buffer_len_;
  732. bind_[9].length = &ipv6_address_buffer_len_;
  733. // prefix_len : TINYINT
  734. bind_[10].buffer_type = MYSQL_TYPE_TINY;
  735. bind_[10].buffer = reinterpret_cast<char*>(&prefix_len_);
  736. bind_[10].is_unsigned = MLM_TRUE;
  737. // (reservation) type : TINYINT
  738. reserv_type_null_ = MLM_FALSE;
  739. bind_[11].buffer_type = MYSQL_TYPE_TINY;
  740. bind_[11].buffer = reinterpret_cast<char*>(&reserv_type_);
  741. bind_[11].is_unsigned = MLM_TRUE;
  742. bind_[11].is_null = &reserv_type_null_;
  743. // dhcp6_iaid INT UNSIGNED
  744. bind_[12].buffer_type = MYSQL_TYPE_LONG;
  745. bind_[12].buffer = reinterpret_cast<char*>(&iaid_);
  746. bind_[12].is_unsigned = MLM_TRUE;
  747. // Add the error flags
  748. setErrorIndicators(bind_, error_);
  749. // Add the data to the vector. Note the end element is one after the
  750. // end of the array.
  751. return (bind_);
  752. };
  753. private:
  754. /// @brief IPv6 reservation type.
  755. uint8_t reserv_type_;
  756. /// @brief Boolean flag indicating if reservation type field is null.
  757. ///
  758. /// This flag is used by the class to determine if the returned row
  759. /// contains IPv6 reservation information.
  760. my_bool reserv_type_null_;
  761. /// @brief Buffer holding IPv6 address/prefix in textual format.
  762. char ipv6_address_buffer_[ADDRESS6_TEXT_MAX_LEN + 1];
  763. /// @brief Length of the textual address representation.
  764. size_t ipv6_address_buffer_len_;
  765. /// @brief Length of the prefix (128 for addresses)
  766. uint8_t prefix_len_;
  767. /// @brief IAID.
  768. uint8_t iaid_;
  769. };
  770. /// @brief This class is used for storing IPv6 reservations in a MySQL database.
  771. ///
  772. /// This class is only used to insert IPv6 reservations into the
  773. /// ipv6_reservations table. It is not used to retrieve IPv6 reservations. To
  774. /// retrieve IPv6 reservation the @ref MySqlIPv6HostExchange class should be
  775. /// used instead.
  776. ///
  777. /// When a new IPv6 reservation is inserted into the database, an appropriate
  778. /// host must be defined in the hosts table. An attempt to insert IPv6
  779. /// reservation for non-existing host will result in failure.
  780. class MySqlIPv6ReservationExchange {
  781. private:
  782. /// @brief Set number of columns for ipv6_reservation table.
  783. static const size_t RESRV_COLUMNS = 6;
  784. public:
  785. /// @brief Constructor
  786. ///
  787. /// Initialize class members representing a single IPv6 reservation.
  788. MySqlIPv6ReservationExchange()
  789. : host_id_(0), address_("::"), address_len_(0), prefix_len_(0), type_(0),
  790. iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
  791. // Reset error table.
  792. std::fill(&error_[0], &error_[RESRV_COLUMNS], MLM_FALSE);
  793. // Set the column names (for error messages)
  794. columns_[0] = "host_id";
  795. columns_[1] = "address";
  796. columns_[2] = "prefix_len";
  797. columns_[3] = "type";
  798. columns_[4] = "dhcp6_iaid";
  799. BOOST_STATIC_ASSERT(4 < RESRV_COLUMNS);
  800. }
  801. /// @brief Create MYSQL_BIND objects for IPv6 Reservation.
  802. ///
  803. /// Fills in the MYSQL_BIND array for sending data in the IPv6 Reservation
  804. /// object to the database.
  805. ///
  806. /// @param resv An object representing IPv6 reservation which will be
  807. /// sent to the database.
  808. /// None of the fields in the reservation are modified -
  809. /// the reservation data is only read.
  810. /// @param id ID of a host owning this reservation
  811. ///
  812. /// @return Vector of MySQL BIND objects representing the data to be added.
  813. std::vector<MYSQL_BIND> createBindForSend(const IPv6Resrv& resv,
  814. const HostID& id) {
  815. // Store the values to ensure they remain valid.
  816. resv_ = resv;
  817. host_id_ = id;
  818. // Initialize prior to constructing the array of MYSQL_BIND structures.
  819. // It sets all fields, including is_null, to zero, so we need to set
  820. // is_null only if it should be true. This gives up minor performance
  821. // benefit while being safe approach. For improved readability, the
  822. // code that explicitly sets is_null is there, but is commented out.
  823. memset(bind_, 0, sizeof(bind_));
  824. // Set up the structures for the various components of the host structure.
  825. try {
  826. // address VARCHAR(39)
  827. address_ = resv.getPrefix().toText();
  828. address_len_ = address_.length();
  829. bind_[0].buffer_type = MYSQL_TYPE_BLOB;
  830. bind_[0].buffer = reinterpret_cast<char*>
  831. (const_cast<char*>(address_.c_str()));
  832. bind_[0].buffer_length = address_len_;
  833. bind_[0].length = &address_len_;
  834. // prefix_len tinyint
  835. prefix_len_ = resv.getPrefixLen();
  836. bind_[1].buffer_type = MYSQL_TYPE_TINY;
  837. bind_[1].buffer = reinterpret_cast<char*>(&prefix_len_);
  838. bind_[1].is_unsigned = MLM_TRUE;
  839. // type tinyint
  840. // See lease6_types for values (0 = IA_NA, 1 = IA_TA, 2 = IA_PD)
  841. type_ = resv.getType() == IPv6Resrv::TYPE_NA ? 0 : 2;
  842. bind_[2].buffer_type = MYSQL_TYPE_TINY;
  843. bind_[2].buffer = reinterpret_cast<char*>(&type_);
  844. bind_[2].is_unsigned = MLM_TRUE;
  845. // dhcp6_iaid INT UNSIGNED
  846. /// @todo: We don't support iaid in the IPv6Resrv yet.
  847. iaid_ = 0;
  848. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  849. bind_[3].buffer = reinterpret_cast<char*>(&iaid_);
  850. bind_[3].is_unsigned = MLM_TRUE;
  851. // host_id INT UNSIGNED NOT NULL
  852. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  853. bind_[4].buffer = reinterpret_cast<char*>(&host_id_);
  854. bind_[4].is_unsigned = MLM_TRUE;
  855. } catch (const std::exception& ex) {
  856. isc_throw(DbOperationError,
  857. "Could not create bind array from IPv6 Reservation: "
  858. << resv_.toText() << ", reason: " << ex.what());
  859. }
  860. // Add the data to the vector. Note the end element is one after the
  861. // end of the array.
  862. // RESRV_COLUMNS -1 as we do not set reservation_id.
  863. return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[RESRV_COLUMNS-1]));
  864. }
  865. private:
  866. /// @brief Host unique identifier.
  867. uint64_t host_id_;
  868. /// @brief Address (or prefix).
  869. std::string address_;
  870. /// @brief Length of the textual address representation.
  871. size_t address_len_;
  872. /// @brief Length of the prefix (128 for addresses).
  873. uint8_t prefix_len_;
  874. /// @brief Reservation type.
  875. uint8_t type_;
  876. /// @brief IAID.
  877. uint8_t iaid_;
  878. /// @brief Object holding reservation being sent to the database.
  879. IPv6Resrv resv_;
  880. /// @brief Array of MySQL bindings.
  881. MYSQL_BIND bind_[RESRV_COLUMNS];
  882. /// @brief Array of strings holding columns names.
  883. std::string columns_[RESRV_COLUMNS];
  884. /// @brief Array of boolean values indicating if error occurred
  885. /// for respective columns.
  886. my_bool error_[RESRV_COLUMNS];
  887. };
  888. } // end of anonymous namespace
  889. namespace isc {
  890. namespace dhcp {
  891. /// @brief Implementation of the @ref MySqlHostDataSource.
  892. class MySqlHostDataSourceImpl {
  893. public:
  894. /// @brief Constructor.
  895. ///
  896. /// This constructor opens database connection and initializes prepared
  897. /// statements used in the queries.
  898. MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters);
  899. /// @brief Destructor.
  900. ~MySqlHostDataSourceImpl();
  901. /// @brief Executes query which inserts a row into one of the tables.
  902. ///
  903. /// @param stindex Index of a statement being executed.
  904. /// @param bind Vector of MYSQL_BIND objects to be used when making the
  905. /// query.
  906. ///
  907. /// @throw isc::dhcp::DuplicateEntry Database throws duplicate entry error
  908. void addQuery(MySqlHostDataSource::StatementIndex stindex,
  909. std::vector<MYSQL_BIND>& bind);
  910. /// @brief Inserts IPv6 Reservation into ipv6_reservation table.
  911. ///
  912. /// @param resv IPv6 Reservation to be added
  913. /// @param id ID of a host owning this reservation
  914. void addResv(const IPv6Resrv& resv, const HostID& id);
  915. /// @brief Check Error and Throw Exception
  916. ///
  917. /// Virtually all MySQL functions return a status which, if non-zero,
  918. /// indicates an error. This inline function conceals a lot of error
  919. /// checking/exception-throwing code.
  920. ///
  921. /// @param status Status code: non-zero implies an error
  922. /// @param index Index of statement that caused the error
  923. /// @param what High-level description of the error
  924. ///
  925. /// @throw isc::dhcp::DbOperationError An operation on the open database
  926. /// has failed.
  927. void checkError(const int status,
  928. const MySqlHostDataSource::StatementIndex index,
  929. const char* what) const;
  930. /// @brief Creates collection of @ref Host objects with associated
  931. /// information such as IPv6 reservations.
  932. ///
  933. /// This method performs a query which returns host information from
  934. /// the 'hosts' table. The query may also use LEFT JOIN clause to
  935. /// retrieve information from other tables, e.g. ipv6_reservations.
  936. /// Whether IPv6 reservations are assigned to the @ref Host objects
  937. /// depends on the type of the exchange object.
  938. ///
  939. /// @param stindex Statement index.
  940. /// @param bind Pointer to an array of MySQL bindings.
  941. /// @param exchange Pointer to the exchange object used for the
  942. /// particular query.
  943. /// @param [out] result Reference to the collection of hosts returned.
  944. /// @param single A boolean value indicating if a single host is
  945. /// expected to be returned, or multiple hosts.
  946. void getHostCollection(MySqlHostDataSource::StatementIndex stindex,
  947. MYSQL_BIND* bind,
  948. boost::shared_ptr<MySqlHostExchange> exchange,
  949. ConstHostCollection& result, bool single) const;
  950. /// @brief Retrieves a host by subnet and client's unique identifier.
  951. ///
  952. /// This method is used by both MySqlHostDataSource::get4 and
  953. /// MySqlHOstDataSource::get6 methods.
  954. ///
  955. /// @param subnet_id Subnet identifier.
  956. /// @param identifier_type Identifier type.
  957. /// @param identifier_begin Pointer to a begining of a buffer containing
  958. /// an identifier.
  959. /// @param identifier_len Identifier length.
  960. /// @param stindex Statement index.
  961. /// @param exchange Pointer to the exchange object used for the
  962. /// particular query.
  963. ///
  964. /// @return Pointer to const instance of Host or null pointer if
  965. /// no host found.
  966. ConstHostPtr getHost(const SubnetID& subnet_id,
  967. const Host::IdentifierType& identifier_type,
  968. const uint8_t* identifier_begin,
  969. const size_t identifier_len,
  970. MySqlHostDataSource::StatementIndex stindex,
  971. boost::shared_ptr<MySqlHostExchange> exchange) const;
  972. /// @brief Pointer to the object representing an exchange which
  973. /// can be used to retrieve DHCPv4 reservation.
  974. boost::shared_ptr<MySqlHostExchange> host_exchange_;
  975. /// @brief Pointer to an object representing an exchange which can
  976. /// be used to retrieve DHCPv6 reservations.
  977. boost::shared_ptr<MySqlHostIPv6Exchange> host_ipv6_exchange_;
  978. /// @brief Pointer to an object representing an exchange which can
  979. /// be used to insert new IPv6 reservation.
  980. boost::shared_ptr<MySqlIPv6ReservationExchange> host_ipv6_reservation_exchange_;
  981. /// @brief MySQL connection
  982. MySqlConnection conn_;
  983. };
  984. MySqlHostDataSourceImpl::
  985. MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
  986. : host_exchange_(new MySqlHostExchange()),
  987. host_ipv6_exchange_(new MySqlHostIPv6Exchange()),
  988. host_ipv6_reservation_exchange_(new MySqlIPv6ReservationExchange()),
  989. conn_(parameters) {
  990. // Open the database.
  991. conn_.openDatabase();
  992. // Enable autocommit. To avoid a flush to disk on every commit, the global
  993. // parameter innodb_flush_log_at_trx_commit should be set to 2. This will
  994. // cause the changes to be written to the log, but flushed to disk in the
  995. // background every second. Setting the parameter to that value will speed
  996. // up the system, but at the risk of losing data if the system crashes.
  997. my_bool result = mysql_autocommit(conn_.mysql_, 1);
  998. if (result != 0) {
  999. isc_throw(DbOperationError, mysql_error(conn_.mysql_));
  1000. }
  1001. // Prepare all statements likely to be used.
  1002. conn_.prepareStatements(tagged_statements,
  1003. MySqlHostDataSource::NUM_STATEMENTS);
  1004. }
  1005. MySqlHostDataSourceImpl::~MySqlHostDataSourceImpl() {
  1006. // Free up the prepared statements, ignoring errors. (What would we do
  1007. // about them? We're destroying this object and are not really concerned
  1008. // with errors on a database connection that is about to go away.)
  1009. for (int i = 0; i < conn_.statements_.size(); ++i) {
  1010. if (conn_.statements_[i] != NULL) {
  1011. (void) mysql_stmt_close(conn_.statements_[i]);
  1012. conn_.statements_[i] = NULL;
  1013. }
  1014. }
  1015. // There is no need to close the database in this destructor: it is
  1016. // closed in the destructor of the mysql_ member variable.
  1017. }
  1018. void
  1019. MySqlHostDataSourceImpl::addQuery(MySqlHostDataSource::StatementIndex stindex,
  1020. std::vector<MYSQL_BIND>& bind) {
  1021. // Bind the parameters to the statement
  1022. int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
  1023. checkError(status, stindex, "unable to bind parameters");
  1024. // Execute the statement
  1025. status = mysql_stmt_execute(conn_.statements_[stindex]);
  1026. if (status != 0) {
  1027. // Failure: check for the special case of duplicate entry.
  1028. if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) {
  1029. isc_throw(DuplicateEntry, "Database duplicate entry error");
  1030. }
  1031. checkError(status, stindex, "unable to execute");
  1032. }
  1033. }
  1034. void
  1035. MySqlHostDataSourceImpl::addResv(const IPv6Resrv& resv,
  1036. const HostID& id) {
  1037. std::vector<MYSQL_BIND> bind =
  1038. host_ipv6_reservation_exchange_->createBindForSend(resv, id);
  1039. addQuery(MySqlHostDataSource::INSERT_V6_RESRV, bind);
  1040. }
  1041. void
  1042. MySqlHostDataSourceImpl::
  1043. checkError(const int status, const MySqlHostDataSource::StatementIndex index,
  1044. const char* what) const {
  1045. if (status != 0) {
  1046. isc_throw(DbOperationError, what << " for <"
  1047. << conn_.text_statements_[index] << ">, reason: "
  1048. << mysql_error(conn_.mysql_) << " (error code "
  1049. << mysql_errno(conn_.mysql_) << ")");
  1050. }
  1051. }
  1052. void
  1053. MySqlHostDataSourceImpl::
  1054. getHostCollection(MySqlHostDataSource::StatementIndex stindex,
  1055. MYSQL_BIND* bind, boost::shared_ptr<MySqlHostExchange> exchange,
  1056. ConstHostCollection& result, bool single) const {
  1057. // Bind the selection parameters to the statement
  1058. int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
  1059. checkError(status, stindex, "unable to bind WHERE clause parameter");
  1060. // Set up the MYSQL_BIND array for the data being returned and bind it to
  1061. // the statement.
  1062. std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
  1063. status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
  1064. checkError(status, stindex, "unable to bind SELECT clause parameters");
  1065. // Execute the statement
  1066. status = mysql_stmt_execute(conn_.statements_[stindex]);
  1067. checkError(status, stindex, "unable to execute");
  1068. // Ensure that all the lease information is retrieved in one go to avoid
  1069. // overhead of going back and forth between client and server.
  1070. status = mysql_stmt_store_result(conn_.statements_[stindex]);
  1071. checkError(status, stindex, "unable to set up for storing all results");
  1072. // Set up the fetch "release" object to release resources associated
  1073. // with the call to mysql_stmt_fetch when this method exits, then
  1074. // retrieve the data. mysql_stmt_fetch return value equal to 0 represents
  1075. // successful data fetch.
  1076. MySqlFreeResult fetch_release(conn_.statements_[stindex]);
  1077. while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) ==
  1078. MLM_MYSQL_FETCH_SUCCESS) {
  1079. try {
  1080. exchange->processFetchedData(result);
  1081. } catch (const isc::BadValue& ex) {
  1082. // Rethrow the exception with a bit more data.
  1083. isc_throw(BadValue, ex.what() << ". Statement is <" <<
  1084. conn_.text_statements_[stindex] << ">");
  1085. }
  1086. if (single && (result.size() > 1)) {
  1087. isc_throw(MultipleRecords, "multiple records were found in the "
  1088. "database where only one was expected for query "
  1089. << conn_.text_statements_[stindex]);
  1090. }
  1091. }
  1092. // How did the fetch end?
  1093. // If mysql_stmt_fetch return value is equal to 1 an error occurred.
  1094. if (status == MLM_MYSQL_FETCH_FAILURE) {
  1095. // Error - unable to fetch results
  1096. checkError(status, stindex, "unable to fetch results");
  1097. } else if (status == MYSQL_DATA_TRUNCATED) {
  1098. // Data truncated - throw an exception indicating what was at fault
  1099. isc_throw(DataTruncated, conn_.text_statements_[stindex]
  1100. << " returned truncated data: columns affected are "
  1101. << exchange->getErrorColumns());
  1102. }
  1103. }
  1104. ConstHostPtr
  1105. MySqlHostDataSourceImpl::
  1106. getHost(const SubnetID& subnet_id,
  1107. const Host::IdentifierType& identifier_type,
  1108. const uint8_t* identifier_begin,
  1109. const size_t identifier_len,
  1110. MySqlHostDataSource::StatementIndex stindex,
  1111. boost::shared_ptr<MySqlHostExchange> exchange) const {
  1112. // Set up the WHERE clause value
  1113. MYSQL_BIND inbind[3];
  1114. memset(inbind, 0, sizeof(inbind));
  1115. uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
  1116. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1117. inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
  1118. inbind[0].is_unsigned = MLM_TRUE;
  1119. // Identifier value.
  1120. std::vector<char> identifier_vec(identifier_begin,
  1121. identifier_begin + identifier_len);
  1122. unsigned long length = identifier_vec.size();
  1123. inbind[2].buffer_type = MYSQL_TYPE_BLOB;
  1124. inbind[2].buffer = &identifier_vec[0];
  1125. inbind[2].buffer_length = length;
  1126. inbind[2].length = &length;
  1127. // Identifier type.
  1128. char identifier_type_copy = static_cast<char>(identifier_type);
  1129. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  1130. inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
  1131. inbind[1].is_unsigned = MLM_TRUE;
  1132. ConstHostCollection collection;
  1133. getHostCollection(stindex, inbind, exchange, collection, true);
  1134. // Return single record if present, else clear the host.
  1135. ConstHostPtr result;
  1136. if (!collection.empty())
  1137. result = *collection.begin();
  1138. return (result);
  1139. }
  1140. MySqlHostDataSource::
  1141. MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
  1142. : impl_(new MySqlHostDataSourceImpl(parameters)) {
  1143. }
  1144. MySqlHostDataSource::~MySqlHostDataSource() {
  1145. delete impl_;
  1146. }
  1147. void
  1148. MySqlHostDataSource::add(const HostPtr& host) {
  1149. // Create the MYSQL_BIND array for the host
  1150. std::vector<MYSQL_BIND> bind = impl_->host_exchange_->createBindForSend(host);
  1151. // ... and call addHost() code.
  1152. impl_->addQuery(INSERT_HOST, bind);
  1153. IPv6ResrvRange v6resv = host->getIPv6Reservations();
  1154. if (std::distance(v6resv.first, v6resv.second) == 0) {
  1155. // If there are no v6 reservations, we're done here.
  1156. return;
  1157. }
  1158. // Gets the last inserted hosts id
  1159. uint64_t host_id = mysql_insert_id(impl_->conn_.mysql_);
  1160. for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
  1161. ++resv) {
  1162. impl_->addResv(resv->second, host_id);
  1163. }
  1164. }
  1165. ConstHostCollection
  1166. MySqlHostDataSource::getAll(const HWAddrPtr& hwaddr,
  1167. const DuidPtr& duid) const {
  1168. if (duid){
  1169. return (getAll(Host::IDENT_DUID, &duid->getDuid()[0],
  1170. duid->getDuid().size()));
  1171. } else if (hwaddr) {
  1172. return (getAll(Host::IDENT_HWADDR,
  1173. &hwaddr->hwaddr_[0],
  1174. hwaddr->hwaddr_.size()));
  1175. }
  1176. return (ConstHostCollection());
  1177. }
  1178. ConstHostCollection
  1179. MySqlHostDataSource::getAll(const Host::IdentifierType& identifier_type,
  1180. const uint8_t* identifier_begin,
  1181. const size_t identifier_len) const {
  1182. // Set up the WHERE clause value
  1183. MYSQL_BIND inbind[2];
  1184. memset(inbind, 0, sizeof(inbind));
  1185. // Identifier type.
  1186. char identifier_type_copy = static_cast<char>(identifier_type);
  1187. inbind[1].buffer = &identifier_type_copy;
  1188. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  1189. inbind[1].is_unsigned = MLM_TRUE;
  1190. // Identifier value.
  1191. std::vector<char> identifier_vec(identifier_begin,
  1192. identifier_begin + identifier_len);
  1193. unsigned long int length = identifier_vec.size();
  1194. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1195. inbind[0].buffer = &identifier_vec[0];
  1196. inbind[0].buffer_length = length;
  1197. inbind[0].length = &length;
  1198. ConstHostCollection result;
  1199. impl_->getHostCollection(GET_HOST_DHCPID, inbind,
  1200. impl_->host_ipv6_exchange_,
  1201. result, false);
  1202. return (result);
  1203. }
  1204. ConstHostCollection
  1205. MySqlHostDataSource::getAll4(const asiolink::IOAddress& address) const {
  1206. // Set up the WHERE clause value
  1207. MYSQL_BIND inbind[1];
  1208. memset(inbind, 0, sizeof(inbind));
  1209. uint32_t addr4 = static_cast<uint32_t>(address);
  1210. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1211. inbind[0].buffer = reinterpret_cast<char*>(&addr4);
  1212. inbind[0].is_unsigned = MLM_TRUE;
  1213. ConstHostCollection result;
  1214. impl_->getHostCollection(GET_HOST_ADDR, inbind, impl_->host_exchange_,
  1215. result, false);
  1216. return (result);
  1217. }
  1218. ConstHostPtr
  1219. MySqlHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
  1220. const DuidPtr& duid) const {
  1221. /// @todo: Rethink the logic in BaseHostDataSource::get4(subnet, hwaddr, duid)
  1222. if (hwaddr && duid) {
  1223. isc_throw(BadValue, "MySQL host data source get4() called with both"
  1224. " hwaddr and duid, only one of them is allowed");
  1225. }
  1226. if (!hwaddr && !duid) {
  1227. isc_throw(BadValue, "MySQL host data source get4() called with "
  1228. "neither hwaddr or duid specified, one of them is required");
  1229. }
  1230. // Choosing one of the identifiers
  1231. if (hwaddr) {
  1232. return (get4(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
  1233. hwaddr->hwaddr_.size()));
  1234. } else if (duid) {
  1235. return (get4(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
  1236. duid->getDuid().size()));
  1237. }
  1238. return (ConstHostPtr());
  1239. }
  1240. ConstHostPtr
  1241. MySqlHostDataSource::get4(const SubnetID& subnet_id,
  1242. const Host::IdentifierType& identifier_type,
  1243. const uint8_t* identifier_begin,
  1244. const size_t identifier_len) const {
  1245. return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
  1246. identifier_len, GET_HOST_SUBID4_DHCPID,
  1247. impl_->host_exchange_));
  1248. }
  1249. ConstHostPtr
  1250. MySqlHostDataSource::get4(const SubnetID& subnet_id,
  1251. const asiolink::IOAddress& address) const {
  1252. // Set up the WHERE clause value
  1253. MYSQL_BIND inbind[2];
  1254. uint32_t subnet = subnet_id;
  1255. memset(inbind, 0, sizeof(inbind));
  1256. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1257. inbind[0].buffer = reinterpret_cast<char*>(&subnet);
  1258. inbind[0].is_unsigned = MLM_TRUE;
  1259. uint32_t addr4 = static_cast<uint32_t>(address);
  1260. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1261. inbind[1].buffer = reinterpret_cast<char*>(&addr4);
  1262. inbind[1].is_unsigned = MLM_TRUE;
  1263. ConstHostCollection collection;
  1264. impl_->getHostCollection(GET_HOST_SUBID_ADDR, inbind, impl_->host_exchange_,
  1265. collection, true);
  1266. // Return single record if present, else clear the host.
  1267. ConstHostPtr result;
  1268. if (!collection.empty())
  1269. result = *collection.begin();
  1270. return (result);
  1271. }
  1272. ConstHostPtr
  1273. MySqlHostDataSource::get6(const SubnetID& subnet_id, const DuidPtr& duid,
  1274. const HWAddrPtr& hwaddr) const {
  1275. /// @todo: Rethink the logic in BaseHostDataSource::get6(subnet, hwaddr, duid)
  1276. if (hwaddr && duid) {
  1277. isc_throw(BadValue, "MySQL host data source get6() called with both"
  1278. " hwaddr and duid, only one of them is allowed");
  1279. }
  1280. if (!hwaddr && !duid) {
  1281. isc_throw(BadValue, "MySQL host data source get6() called with "
  1282. "neither hwaddr or duid specified, one of them is required");
  1283. }
  1284. // Choosing one of the identifiers
  1285. if (hwaddr) {
  1286. return (get6(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
  1287. hwaddr->hwaddr_.size()));
  1288. } else if (duid) {
  1289. return (get6(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
  1290. duid->getDuid().size()));
  1291. }
  1292. return (ConstHostPtr());
  1293. }
  1294. ConstHostPtr
  1295. MySqlHostDataSource::get6(const SubnetID& subnet_id,
  1296. const Host::IdentifierType& identifier_type,
  1297. const uint8_t* identifier_begin,
  1298. const size_t identifier_len) const {
  1299. return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
  1300. identifier_len, GET_HOST_SUBID6_DHCPID,
  1301. impl_->host_ipv6_exchange_));
  1302. }
  1303. ConstHostPtr
  1304. MySqlHostDataSource::get6(const asiolink::IOAddress& prefix,
  1305. const uint8_t prefix_len) const {
  1306. // Set up the WHERE clause value
  1307. MYSQL_BIND inbind[2];
  1308. memset(inbind, 0, sizeof(inbind));
  1309. std::string addr6 = prefix.toText();
  1310. unsigned long addr6_length = addr6.size();
  1311. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1312. inbind[0].buffer = reinterpret_cast<char*>
  1313. (const_cast<char*>(addr6.c_str()));
  1314. inbind[0].length = &addr6_length;
  1315. inbind[0].buffer_length = addr6_length;
  1316. uint8_t tmp = prefix_len;
  1317. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  1318. inbind[1].buffer = reinterpret_cast<char*>(&tmp);
  1319. inbind[1].is_unsigned = MLM_TRUE;
  1320. ConstHostCollection collection;
  1321. impl_->getHostCollection(GET_HOST_PREFIX, inbind,
  1322. impl_->host_ipv6_exchange_,
  1323. collection, true);
  1324. // Return single record if present, else clear the host.
  1325. ConstHostPtr result;
  1326. if (!collection.empty()) {
  1327. result = *collection.begin();
  1328. }
  1329. return (result);
  1330. }
  1331. // Miscellaneous database methods.
  1332. std::string MySqlHostDataSource::getName() const {
  1333. std::string name = "";
  1334. try {
  1335. name = impl_->conn_.getParameter("name");
  1336. } catch (...) {
  1337. // Return an empty name
  1338. }
  1339. return (name);
  1340. }
  1341. std::string MySqlHostDataSource::getDescription() const {
  1342. return (std::string("Host data source that stores host information"
  1343. "in MySQL database"));
  1344. }
  1345. std::pair<uint32_t, uint32_t> MySqlHostDataSource::getVersion() const {
  1346. const StatementIndex stindex = GET_VERSION;
  1347. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1348. DHCPSRV_MYSQL_HOST_DB_GET_VERSION);
  1349. uint32_t major; // Major version number
  1350. uint32_t minor; // Minor version number
  1351. // Execute the prepared statement
  1352. int status = mysql_stmt_execute(impl_->conn_.statements_[stindex]);
  1353. if (status != 0) {
  1354. isc_throw(DbOperationError, "unable to execute <"
  1355. << impl_->conn_.text_statements_[stindex]
  1356. << "> - reason: " << mysql_error(impl_->conn_.mysql_));
  1357. }
  1358. // Bind the output of the statement to the appropriate variables.
  1359. MYSQL_BIND bind[2];
  1360. memset(bind, 0, sizeof(bind));
  1361. bind[0].buffer_type = MYSQL_TYPE_LONG;
  1362. bind[0].is_unsigned = 1;
  1363. bind[0].buffer = &major;
  1364. bind[0].buffer_length = sizeof(major);
  1365. bind[1].buffer_type = MYSQL_TYPE_LONG;
  1366. bind[1].is_unsigned = 1;
  1367. bind[1].buffer = &minor;
  1368. bind[1].buffer_length = sizeof(minor);
  1369. status = mysql_stmt_bind_result(impl_->conn_.statements_[stindex], bind);
  1370. if (status != 0) {
  1371. isc_throw(DbOperationError, "unable to bind result set: "
  1372. << mysql_error(impl_->conn_.mysql_));
  1373. }
  1374. // Fetch the data and set up the "release" object to release associated
  1375. // resources when this method exits then retrieve the data.
  1376. // mysql_stmt_fetch return value other than 0 means error occurrence.
  1377. MySqlFreeResult fetch_release(impl_->conn_.statements_[stindex]);
  1378. status = mysql_stmt_fetch(impl_->conn_.statements_[stindex]);
  1379. if (status != 0) {
  1380. isc_throw(DbOperationError, "unable to obtain result set: "
  1381. << mysql_error(impl_->conn_.mysql_));
  1382. }
  1383. return (std::make_pair(major, minor));
  1384. }
  1385. void
  1386. MySqlHostDataSource::commit() {
  1387. impl_->conn_.commit();
  1388. }
  1389. void
  1390. MySqlHostDataSource::rollback() {
  1391. impl_->conn_.rollback();
  1392. }
  1393. }; // end of isc::dhcp namespace
  1394. }; // end of isc namespace