mysql_lease_mgr.cc 90 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160
  1. // Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <asiolink/io_address.h>
  16. #include <dhcp/duid.h>
  17. #include <dhcp/hwaddr.h>
  18. #include <dhcpsrv/dhcpsrv_log.h>
  19. #include <dhcpsrv/mysql_lease_mgr.h>
  20. #include <dhcpsrv/mysql_connection.h>
  21. #include <boost/static_assert.hpp>
  22. #include <mysqld_error.h>
  23. #include <errmsg.h>
  24. #include <iostream>
  25. #include <iomanip>
  26. #include <limits.h>
  27. #include <sstream>
  28. #include <string>
  29. #include <time.h>
  30. using namespace isc;
  31. using namespace isc::dhcp;
  32. using namespace std;
  33. /// @file
  34. ///
  35. /// This file holds the implementation of the Lease Manager using MySQL. The
  36. /// implementation uses MySQL's C API, as it comes as standard with the MySQL
  37. /// client libraries.
  38. ///
  39. /// In general, each of the database access methods corresponds to one SQL
  40. /// statement. To avoid the overhead of parsing a statement every time it is
  41. /// used, when the database is opened "prepared statements" are created -
  42. /// essentially doing the SQL parsing up front. Every time a method is used
  43. /// to access data, the corresponding prepared statement is referenced. Each
  44. /// prepared statement contains a set of placeholders for data, each
  45. /// placeholder being for:
  46. ///
  47. /// - data being added to the database (as in adding or updating a lease)
  48. /// - data being retrieved from the database (as in getting lease information)
  49. /// - selection criteria used to determine which records to update/retrieve.
  50. ///
  51. /// All such data is associated with the prepared statment using an array of
  52. /// MYSQL_BIND structures. Each element in the array corresponds to one
  53. /// parameter in the prepared statement - the first element in the array is
  54. /// associated with the first parameter, the second element with the second
  55. /// parameter etc.
  56. ///
  57. /// Within this file, the setting up of the MYSQL_BIND arrays for data being
  58. /// passed to and retrieved from the database is handled in the
  59. /// isc::dhcp::MySqlLease4Exchange and isc::dhcp::MySqlLease6Exchange classes.
  60. /// The classes also hold intermediate variables required for exchanging some
  61. /// of the data.
  62. ///
  63. /// With these exchange objects in place, many of the methods follow similar
  64. /// logic:
  65. /// - Set up the MYSQL_BIND array for data being transferred to/from the
  66. /// database. For data being transferred to the database, some of the
  67. /// data is extracted from the lease to intermediate variables, whilst
  68. /// in other cases the MYSQL_BIND arrays point to the data in the lease.
  69. /// - Set up the MYSQL_BIND array for the data selection parameters.
  70. /// - Bind these arrays to the prepared statement.
  71. /// - Execute the statement.
  72. /// - If there is output, copy the data from the bound variables to the output
  73. /// lease object.
  74. namespace {
  75. /// @brief Maximum length of the hostname stored in DNS.
  76. ///
  77. /// This length is restricted by the length of the domain-name carried
  78. /// in the Client FQDN %Option (see RFC4702 and RFC4704).
  79. const size_t HOSTNAME_MAX_LEN = 255;
  80. /// @brief Maximum size of an IPv6 address represented as a text string.
  81. ///
  82. /// This is 32 hexadecimal characters written in 8 groups of four, plus seven
  83. /// colon separators.
  84. const size_t ADDRESS6_TEXT_MAX_LEN = 39;
  85. TaggedStatement tagged_statements[] = {
  86. {MySqlLeaseMgr::DELETE_LEASE4,
  87. "DELETE FROM lease4 WHERE address = ?"},
  88. {MySqlLeaseMgr::DELETE_LEASE4_STATE_EXPIRED,
  89. "DELETE FROM lease4 "
  90. "WHERE state = ? AND expire < ?"},
  91. {MySqlLeaseMgr::DELETE_LEASE6,
  92. "DELETE FROM lease6 WHERE address = ?"},
  93. {MySqlLeaseMgr::DELETE_LEASE6_STATE_EXPIRED,
  94. "DELETE FROM lease6 "
  95. "WHERE state = ? AND expire < ?"},
  96. {MySqlLeaseMgr::GET_LEASE4_ADDR,
  97. "SELECT address, hwaddr, client_id, "
  98. "valid_lifetime, expire, subnet_id, "
  99. "fqdn_fwd, fqdn_rev, hostname, "
  100. "state "
  101. "FROM lease4 "
  102. "WHERE address = ?"},
  103. {MySqlLeaseMgr::GET_LEASE4_CLIENTID,
  104. "SELECT address, hwaddr, client_id, "
  105. "valid_lifetime, expire, subnet_id, "
  106. "fqdn_fwd, fqdn_rev, hostname, "
  107. "state "
  108. "FROM lease4 "
  109. "WHERE client_id = ?"},
  110. {MySqlLeaseMgr::GET_LEASE4_CLIENTID_SUBID,
  111. "SELECT address, hwaddr, client_id, "
  112. "valid_lifetime, expire, subnet_id, "
  113. "fqdn_fwd, fqdn_rev, hostname, "
  114. "state "
  115. "FROM lease4 "
  116. "WHERE client_id = ? AND subnet_id = ?"},
  117. {MySqlLeaseMgr::GET_LEASE4_HWADDR,
  118. "SELECT address, hwaddr, client_id, "
  119. "valid_lifetime, expire, subnet_id, "
  120. "fqdn_fwd, fqdn_rev, hostname, "
  121. "state "
  122. "FROM lease4 "
  123. "WHERE hwaddr = ?"},
  124. {MySqlLeaseMgr::GET_LEASE4_HWADDR_SUBID,
  125. "SELECT address, hwaddr, client_id, "
  126. "valid_lifetime, expire, subnet_id, "
  127. "fqdn_fwd, fqdn_rev, hostname, "
  128. "state "
  129. "FROM lease4 "
  130. "WHERE hwaddr = ? AND subnet_id = ?"},
  131. {MySqlLeaseMgr::GET_LEASE4_EXPIRE,
  132. "SELECT address, hwaddr, client_id, "
  133. "valid_lifetime, expire, subnet_id, "
  134. "fqdn_fwd, fqdn_rev, hostname, "
  135. "state "
  136. "FROM lease4 "
  137. "WHERE state != ? AND expire < ? "
  138. "ORDER BY expire ASC "
  139. "LIMIT ?"},
  140. {MySqlLeaseMgr::GET_LEASE6_ADDR,
  141. "SELECT address, duid, valid_lifetime, "
  142. "expire, subnet_id, pref_lifetime, "
  143. "lease_type, iaid, prefix_len, "
  144. "fqdn_fwd, fqdn_rev, hostname, "
  145. "hwaddr, hwtype, hwaddr_source, "
  146. "state "
  147. "FROM lease6 "
  148. "WHERE address = ? AND lease_type = ?"},
  149. {MySqlLeaseMgr::GET_LEASE6_DUID_IAID,
  150. "SELECT address, duid, valid_lifetime, "
  151. "expire, subnet_id, pref_lifetime, "
  152. "lease_type, iaid, prefix_len, "
  153. "fqdn_fwd, fqdn_rev, hostname, "
  154. "hwaddr, hwtype, hwaddr_source, "
  155. "state "
  156. "FROM lease6 "
  157. "WHERE duid = ? AND iaid = ? AND lease_type = ?"},
  158. {MySqlLeaseMgr::GET_LEASE6_DUID_IAID_SUBID,
  159. "SELECT address, duid, valid_lifetime, "
  160. "expire, subnet_id, pref_lifetime, "
  161. "lease_type, iaid, prefix_len, "
  162. "fqdn_fwd, fqdn_rev, hostname, "
  163. "hwaddr, hwtype, hwaddr_source, "
  164. "state "
  165. "FROM lease6 "
  166. "WHERE duid = ? AND iaid = ? AND subnet_id = ? "
  167. "AND lease_type = ?"},
  168. {MySqlLeaseMgr::GET_LEASE6_EXPIRE,
  169. "SELECT address, duid, valid_lifetime, "
  170. "expire, subnet_id, pref_lifetime, "
  171. "lease_type, iaid, prefix_len, "
  172. "fqdn_fwd, fqdn_rev, hostname, "
  173. "hwaddr, hwtype, hwaddr_source, "
  174. "state "
  175. "FROM lease6 "
  176. "WHERE state != ? AND expire < ? "
  177. "ORDER BY expire ASC "
  178. "LIMIT ?"},
  179. {MySqlLeaseMgr::GET_VERSION,
  180. "SELECT version, minor FROM schema_version"},
  181. {MySqlLeaseMgr::INSERT_LEASE4,
  182. "INSERT INTO lease4(address, hwaddr, client_id, "
  183. "valid_lifetime, expire, subnet_id, "
  184. "fqdn_fwd, fqdn_rev, hostname, state) "
  185. "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
  186. {MySqlLeaseMgr::INSERT_LEASE6,
  187. "INSERT INTO lease6(address, duid, valid_lifetime, "
  188. "expire, subnet_id, pref_lifetime, "
  189. "lease_type, iaid, prefix_len, "
  190. "fqdn_fwd, fqdn_rev, hostname, "
  191. "hwaddr, hwtype, hwaddr_source, "
  192. "state) "
  193. "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"},
  194. {MySqlLeaseMgr::UPDATE_LEASE4,
  195. "UPDATE lease4 SET address = ?, hwaddr = ?, "
  196. "client_id = ?, valid_lifetime = ?, expire = ?, "
  197. "subnet_id = ?, fqdn_fwd = ?, fqdn_rev = ?, "
  198. "hostname = ?, state = ? "
  199. "WHERE address = ?"},
  200. {MySqlLeaseMgr::UPDATE_LEASE6,
  201. "UPDATE lease6 SET address = ?, duid = ?, "
  202. "valid_lifetime = ?, expire = ?, subnet_id = ?, "
  203. "pref_lifetime = ?, lease_type = ?, iaid = ?, "
  204. "prefix_len = ?, fqdn_fwd = ?, fqdn_rev = ?, "
  205. "hostname = ?, hwaddr = ?, hwtype = ?, hwaddr_source = ?, "
  206. "state = ? "
  207. "WHERE address = ?"},
  208. // End of list sentinel
  209. {MySqlLeaseMgr::NUM_STATEMENTS, NULL}
  210. };
  211. };
  212. namespace isc {
  213. namespace dhcp {
  214. /// @brief Common MySQL and Lease Data Methods
  215. ///
  216. /// The MySqlLease4Exchange and MySqlLease6Exchange classes provide the
  217. /// functionaility to set up binding information between variables in the
  218. /// program and data extracted from the database. This class is the common
  219. /// base to both of them, containing some common methods.
  220. class MySqlLeaseExchange {
  221. public:
  222. /// @brief Set error indicators
  223. ///
  224. /// Sets the error indicator for each of the MYSQL_BIND elements. It points
  225. /// the "error" field within an element of the input array to the
  226. /// corresponding element of the passed error array.
  227. ///
  228. /// @param bind Array of BIND elements
  229. /// @param error Array of error elements. If there is an error in getting
  230. /// data associated with one of the "bind" elements, the
  231. /// corresponding element in the error array is set to MLM_TRUE.
  232. /// @param count Size of each of the arrays.
  233. static void setErrorIndicators(MYSQL_BIND* bind, my_bool* error,
  234. size_t count) {
  235. for (size_t i = 0; i < count; ++i) {
  236. error[i] = MLM_FALSE;
  237. bind[i].error = reinterpret_cast<char*>(&error[i]);
  238. }
  239. }
  240. /// @brief Return columns in error
  241. ///
  242. /// If an error is returned from a fetch (in particular, a truncated
  243. /// status), this method can be called to get the names of the fields in
  244. /// error. It returns a string comprising the names of the fields
  245. /// separated by commas. In the case of there being no error indicators
  246. /// set, it returns the string "(None)".
  247. ///
  248. /// @param error Array of error elements. An element is set to MLM_TRUE
  249. /// if the corresponding column in the database is the source of
  250. /// the error.
  251. /// @param names Array of column names, the same size as the error array.
  252. /// @param count Size of each of the arrays.
  253. static std::string getColumnsInError(my_bool* error, std::string* names,
  254. size_t count) {
  255. std::string result = "";
  256. // Accumulate list of column names
  257. for (size_t i = 0; i < count; ++i) {
  258. if (error[i] == MLM_TRUE) {
  259. if (!result.empty()) {
  260. result += ", ";
  261. }
  262. result += names[i];
  263. }
  264. }
  265. if (result.empty()) {
  266. result = "(None)";
  267. }
  268. return (result);
  269. }
  270. };
  271. /// @brief Exchange MySQL and Lease4 Data
  272. ///
  273. /// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
  274. /// describe the parameters in the prepared statements. Where information is
  275. /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
  276. /// structure is identical. This class handles the creation of that array.
  277. ///
  278. /// Owing to the MySQL API, the process requires some intermediate variables
  279. /// to hold things like data length etc. This object holds those variables.
  280. ///
  281. /// @note There are no unit tests for this class. It is tested indirectly
  282. /// in all MySqlLeaseMgr::xxx4() calls where it is used.
  283. class MySqlLease4Exchange : public MySqlLeaseExchange {
  284. /// @brief Set number of database columns for this lease structure
  285. static const size_t LEASE_COLUMNS = 10;
  286. public:
  287. /// @brief Constructor
  288. ///
  289. /// The initialization of the variables here is only to satisfy cppcheck -
  290. /// all variables are initialized/set in the methods before they are used.
  291. MySqlLease4Exchange() : addr4_(0), hwaddr_length_(0), client_id_length_(0),
  292. client_id_null_(MLM_FALSE),
  293. subnet_id_(0), valid_lifetime_(0),
  294. fqdn_fwd_(false), fqdn_rev_(false), hostname_length_(0),
  295. state_(0) {
  296. memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
  297. memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
  298. memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
  299. std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
  300. // Set the column names (for error messages)
  301. columns_[0] = "address";
  302. columns_[1] = "hwaddr";
  303. columns_[2] = "client_id";
  304. columns_[3] = "valid_lifetime";
  305. columns_[4] = "expire";
  306. columns_[5] = "subnet_id";
  307. columns_[6] = "fqdn_fwd";
  308. columns_[7] = "fqdn_rev";
  309. columns_[8] = "hostname";
  310. columns_[9] = "state";
  311. BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS);
  312. }
  313. /// @brief Create MYSQL_BIND objects for Lease4 Pointer
  314. ///
  315. /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
  316. /// the database.
  317. ///
  318. /// @param lease Lease object to be added to the database. None of the
  319. /// fields in the lease are modified - the lease data is only read.
  320. ///
  321. /// @return Vector of MySQL BIND objects representing the data to be added.
  322. std::vector<MYSQL_BIND> createBindForSend(const Lease4Ptr& lease) {
  323. // Store lease object to ensure it remains valid.
  324. lease_ = lease;
  325. // Initialize prior to constructing the array of MYSQL_BIND structures.
  326. // It sets all fields, including is_null, to zero, so we need to set
  327. // is_null only if it should be true. This gives up minor performance
  328. // benefit while being safe approach. For improved readability, the
  329. // code that explicitly sets is_null is there, but is commented out.
  330. memset(bind_, 0, sizeof(bind_));
  331. // Set up the structures for the various components of the lease4
  332. // structure.
  333. try {
  334. // Address: uint32_t
  335. // The address in the Lease structure is an IOAddress object. Convert
  336. // this to an integer for storage.
  337. addr4_ = static_cast<uint32_t>(lease_->addr_);
  338. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  339. bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
  340. bind_[0].is_unsigned = MLM_TRUE;
  341. // bind_[0].is_null = &MLM_FALSE; // commented out for performance
  342. // reasons, see memset() above
  343. // hwaddr: varbinary(128)
  344. // For speed, we avoid copying the data into temporary storage and
  345. // instead extract it from the lease structure directly.
  346. hwaddr_length_ = lease_->hwaddr_->hwaddr_.size();
  347. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  348. bind_[1].buffer = reinterpret_cast<char*>(&(lease_->hwaddr_->hwaddr_[0]));
  349. bind_[1].buffer_length = hwaddr_length_;
  350. bind_[1].length = &hwaddr_length_;
  351. // bind_[1].is_null = &MLM_FALSE; // commented out for performance
  352. // reasons, see memset() above
  353. // client_id: varbinary(128)
  354. if (lease_->client_id_) {
  355. client_id_ = lease_->client_id_->getClientId();
  356. client_id_length_ = client_id_.size();
  357. bind_[2].buffer_type = MYSQL_TYPE_BLOB;
  358. bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
  359. bind_[2].buffer_length = client_id_length_;
  360. bind_[2].length = &client_id_length_;
  361. // bind_[2].is_null = &MLM_FALSE; // commented out for performance
  362. // reasons, see memset() above
  363. } else {
  364. bind_[2].buffer_type = MYSQL_TYPE_NULL;
  365. // According to http://dev.mysql.com/doc/refman/5.5/en/
  366. // c-api-prepared-statement-data-structures.html, the other
  367. // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
  368. // but let's set them to some sane values in case earlier versions
  369. // didn't have that assumption.
  370. client_id_null_ = MLM_TRUE;
  371. bind_[2].buffer = NULL;
  372. bind_[2].is_null = &client_id_null_;
  373. }
  374. // valid lifetime: unsigned int
  375. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  376. bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
  377. bind_[3].is_unsigned = MLM_TRUE;
  378. // bind_[3].is_null = &MLM_FALSE; // commented out for performance
  379. // reasons, see memset() above
  380. // expire: timestamp
  381. // The lease structure holds the client last transmission time (cltt_)
  382. // For convenience for external tools, this is converted to lease
  383. // expiry time (expire). The relationship is given by:
  384. //
  385. // expire = cltt_ + valid_lft_
  386. MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
  387. expire_);
  388. bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
  389. bind_[4].buffer = reinterpret_cast<char*>(&expire_);
  390. bind_[4].buffer_length = sizeof(expire_);
  391. // bind_[4].is_null = &MLM_FALSE; // commented out for performance
  392. // reasons, see memset() above
  393. // subnet_id: unsigned int
  394. // Can use lease_->subnet_id_ directly as it is of type uint32_t.
  395. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  396. bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
  397. bind_[5].is_unsigned = MLM_TRUE;
  398. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  399. // reasons, see memset() above
  400. // fqdn_fwd: boolean
  401. bind_[6].buffer_type = MYSQL_TYPE_TINY;
  402. bind_[6].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
  403. bind_[6].is_unsigned = MLM_TRUE;
  404. // bind_[6].is_null = &MLM_FALSE; // commented out for performance
  405. // reasons, see memset() above
  406. // fqdn_rev: boolean
  407. bind_[7].buffer_type = MYSQL_TYPE_TINY;
  408. bind_[7].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
  409. bind_[7].is_unsigned = MLM_TRUE;
  410. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  411. // reasons, see memset() above
  412. // hostname: varchar(255)
  413. bind_[8].buffer_type = MYSQL_TYPE_VARCHAR;
  414. bind_[8].buffer = const_cast<char*>(lease_->hostname_.c_str());
  415. bind_[8].buffer_length = lease_->hostname_.length();
  416. // bind_[8].is_null = &MLM_FALSE; // commented out for performance
  417. // reasons, see memset() above
  418. // state: uint32_t.
  419. bind_[9].buffer_type = MYSQL_TYPE_LONG;
  420. bind_[9].buffer = reinterpret_cast<char*>(&lease_->state_);
  421. bind_[9].is_unsigned = MLM_TRUE;
  422. // bind_[9].is_null = &MLM_FALSE; // commented out for performance
  423. // reasons, see memset() above
  424. // Add the error flags
  425. setErrorIndicators(bind_, error_, LEASE_COLUMNS);
  426. // .. and check that we have the numbers correct at compile time.
  427. BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS);
  428. } catch (const std::exception& ex) {
  429. isc_throw(DbOperationError,
  430. "Could not create bind array from Lease4: "
  431. << lease_->addr_.toText() << ", reason: " << ex.what());
  432. }
  433. // Add the data to the vector. Note the end element is one after the
  434. // end of the array.
  435. return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
  436. }
  437. /// @brief Create BIND array to receive data
  438. ///
  439. /// Creates a MYSQL_BIND array to receive Lease4 data from the database.
  440. /// After data is successfully received, getLeaseData() can be used to copy
  441. /// it to a Lease6 object.
  442. ///
  443. std::vector<MYSQL_BIND> createBindForReceive() {
  444. // Initialize MYSQL_BIND array.
  445. // It sets all fields, including is_null, to zero, so we need to set
  446. // is_null only if it should be true. This gives up minor performance
  447. // benefit while being safe approach. For improved readability, the
  448. // code that explicitly sets is_null is there, but is commented out.
  449. memset(bind_, 0, sizeof(bind_));
  450. // address: uint32_t
  451. bind_[0].buffer_type = MYSQL_TYPE_LONG;
  452. bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
  453. bind_[0].is_unsigned = MLM_TRUE;
  454. // bind_[0].is_null = &MLM_FALSE; // commented out for performance
  455. // reasons, see memset() above
  456. // hwaddr: varbinary(20)
  457. hwaddr_length_ = sizeof(hwaddr_buffer_);
  458. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  459. bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
  460. bind_[1].buffer_length = hwaddr_length_;
  461. bind_[1].length = &hwaddr_length_;
  462. // bind_[1].is_null = &MLM_FALSE; // commented out for performance
  463. // reasons, see memset() above
  464. // client_id: varbinary(128)
  465. client_id_length_ = sizeof(client_id_buffer_);
  466. bind_[2].buffer_type = MYSQL_TYPE_BLOB;
  467. bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
  468. bind_[2].buffer_length = client_id_length_;
  469. bind_[2].length = &client_id_length_;
  470. bind_[2].is_null = &client_id_null_;
  471. // bind_[2].is_null = &MLM_FALSE; // commented out for performance
  472. // reasons, see memset() above
  473. // lease_time: unsigned int
  474. bind_[3].buffer_type = MYSQL_TYPE_LONG;
  475. bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
  476. bind_[3].is_unsigned = MLM_TRUE;
  477. // bind_[3].is_null = &MLM_FALSE; // commented out for performance
  478. // reasons, see memset() above
  479. // expire: timestamp
  480. bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
  481. bind_[4].buffer = reinterpret_cast<char*>(&expire_);
  482. bind_[4].buffer_length = sizeof(expire_);
  483. // bind_[4].is_null = &MLM_FALSE; // commented out for performance
  484. // reasons, see memset() above
  485. // subnet_id: unsigned int
  486. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  487. bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
  488. bind_[5].is_unsigned = MLM_TRUE;
  489. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  490. // reasons, see memset() above
  491. // fqdn_fwd: boolean
  492. bind_[6].buffer_type = MYSQL_TYPE_TINY;
  493. bind_[6].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
  494. bind_[6].is_unsigned = MLM_TRUE;
  495. // bind_[6].is_null = &MLM_FALSE; // commented out for performance
  496. // reasons, see memset() above
  497. // fqdn_rev: boolean
  498. bind_[7].buffer_type = MYSQL_TYPE_TINY;
  499. bind_[7].buffer = reinterpret_cast<char*>(&fqdn_rev_);
  500. bind_[7].is_unsigned = MLM_TRUE;
  501. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  502. // reasons, see memset() above
  503. // hostname: varchar(255)
  504. hostname_length_ = sizeof(hostname_buffer_);
  505. bind_[8].buffer_type = MYSQL_TYPE_STRING;
  506. bind_[8].buffer = reinterpret_cast<char*>(hostname_buffer_);
  507. bind_[8].buffer_length = hostname_length_;
  508. bind_[8].length = &hostname_length_;
  509. // bind_[8].is_null = &MLM_FALSE; // commented out for performance
  510. // reasons, see memset() above
  511. // state: uint32_t
  512. bind_[9].buffer_type = MYSQL_TYPE_LONG;
  513. bind_[9].buffer = reinterpret_cast<char*>(&state_);
  514. bind_[9].is_unsigned = MLM_TRUE;
  515. // bind_[9].is_null = &MLM_FALSE; // commented out for performance
  516. // reasons, see memset() above
  517. // Add the error flags
  518. setErrorIndicators(bind_, error_, LEASE_COLUMNS);
  519. // .. and check that we have the numbers correct at compile time.
  520. BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS);
  521. // Add the data to the vector. Note the end element is one after the
  522. // end of the array.
  523. return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
  524. }
  525. /// @brief Copy Received Data into Lease4 Object
  526. ///
  527. /// Called after the MYSQL_BIND array created by createBindForReceive()
  528. /// has been used, this copies data from the internal member variables
  529. /// into a Lease4 object.
  530. ///
  531. /// @return Lease4Ptr Pointer to a Lease6 object holding the relevant
  532. /// data.
  533. Lease4Ptr getLeaseData() {
  534. // Convert times received from the database to times for the lease
  535. // structure
  536. time_t cltt = 0;
  537. MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
  538. if (client_id_null_==MLM_TRUE) {
  539. // There's no client-id, so we pass client-id_length_ set to 0
  540. client_id_length_ = 0;
  541. }
  542. // Hostname is passed to Lease4 as a string object. We have to create
  543. // it from the buffer holding hostname and the buffer length.
  544. std::string hostname(hostname_buffer_,
  545. hostname_buffer_ + hostname_length_);
  546. // Recreate the hardware address.
  547. HWAddrPtr hwaddr(new HWAddr(hwaddr_buffer_, hwaddr_length_, HTYPE_ETHER));
  548. // note that T1 and T2 are not stored
  549. Lease4Ptr lease(new Lease4(addr4_, hwaddr,
  550. client_id_buffer_, client_id_length_,
  551. valid_lifetime_, 0, 0, cltt, subnet_id_,
  552. fqdn_fwd_, fqdn_rev_, hostname));
  553. lease->state_ = state_;
  554. return (lease);
  555. }
  556. /// @brief Return columns in error
  557. ///
  558. /// If an error is returned from a fetch (in particular, a truncated
  559. /// status), this method can be called to get the names of the fields in
  560. /// error. It returns a string comprising the names of the fields
  561. /// separated by commas. In the case of there being no error indicators
  562. /// set, it returns the string "(None)".
  563. ///
  564. /// @return Comma-separated list of columns in error, or the string
  565. /// "(None)".
  566. std::string getErrorColumns() {
  567. return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
  568. }
  569. private:
  570. // Note: All array lengths are equal to the corresponding variable in the
  571. // schema.
  572. // Note: Arrays are declared fixed length for speed of creation
  573. uint32_t addr4_; ///< IPv4 address
  574. MYSQL_BIND bind_[LEASE_COLUMNS]; ///< Bind array
  575. std::string columns_[LEASE_COLUMNS];///< Column names
  576. my_bool error_[LEASE_COLUMNS]; ///< Error array
  577. std::vector<uint8_t> hwaddr_; ///< Hardware address
  578. uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
  579. ///< Hardware address buffer
  580. unsigned long hwaddr_length_; ///< Hardware address length
  581. std::vector<uint8_t> client_id_; ///< Client identification
  582. uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
  583. ///< Client ID buffer
  584. unsigned long client_id_length_; ///< Client ID address length
  585. my_bool client_id_null_; ///< Is Client ID null?
  586. MYSQL_TIME expire_; ///< Lease expiry time
  587. Lease4Ptr lease_; ///< Pointer to lease object
  588. uint32_t subnet_id_; ///< Subnet identification
  589. uint32_t valid_lifetime_; ///< Lease time
  590. my_bool fqdn_fwd_; ///< Has forward DNS update been
  591. ///< performed
  592. my_bool fqdn_rev_; ///< Has reverse DNS update been
  593. ///< performed
  594. char hostname_buffer_[HOSTNAME_MAX_LEN];
  595. ///< Client hostname
  596. unsigned long hostname_length_; ///< Client hostname length
  597. uint32_t state_; ///< Lease state
  598. };
  599. /// @brief Exchange MySQL and Lease6 Data
  600. ///
  601. /// On any MySQL operation, arrays of MYSQL_BIND structures must be built to
  602. /// describe the parameters in the prepared statements. Where information is
  603. /// inserted or retrieved - INSERT, UPDATE, SELECT - a large amount of that
  604. /// structure is identical. This class handles the creation of that array.
  605. ///
  606. /// Owing to the MySQL API, the process requires some intermediate variables
  607. /// to hold things like data length etc. This object holds those variables.
  608. ///
  609. /// @note There are no unit tests for this class. It is tested indirectly
  610. /// in all MySqlLeaseMgr::xxx6() calls where it is used.
  611. class MySqlLease6Exchange : public MySqlLeaseExchange {
  612. /// @brief Set number of database columns for this lease structure
  613. static const size_t LEASE_COLUMNS = 16;
  614. public:
  615. /// @brief Constructor
  616. ///
  617. /// The initialization of the variables here is nonly to satisfy cppcheck -
  618. /// all variables are initialized/set in the methods before they are used.
  619. MySqlLease6Exchange() : addr6_length_(0), duid_length_(0),
  620. iaid_(0), lease_type_(0), prefixlen_(0),
  621. pref_lifetime_(0), subnet_id_(0), valid_lifetime_(0),
  622. fqdn_fwd_(false), fqdn_rev_(false),
  623. hostname_length_(0), hwaddr_length_(0),
  624. hwaddr_null_(MLM_FALSE), hwtype_(0), hwaddr_source_(0),
  625. state_(0) {
  626. memset(addr6_buffer_, 0, sizeof(addr6_buffer_));
  627. memset(duid_buffer_, 0, sizeof(duid_buffer_));
  628. memset(hostname_buffer_, 0, sizeof(hostname_buffer_));
  629. memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
  630. std::fill(&error_[0], &error_[LEASE_COLUMNS], MLM_FALSE);
  631. // Set the column names (for error messages)
  632. columns_[0] = "address";
  633. columns_[1] = "duid";
  634. columns_[2] = "valid_lifetime";
  635. columns_[3] = "expire";
  636. columns_[4] = "subnet_id";
  637. columns_[5] = "pref_lifetime";
  638. columns_[6] = "lease_type";
  639. columns_[7] = "iaid";
  640. columns_[8] = "prefix_len";
  641. columns_[9] = "fqdn_fwd";
  642. columns_[10] = "fqdn_rev";
  643. columns_[11] = "hostname";
  644. columns_[12] = "hwaddr";
  645. columns_[13] = "hwtype";
  646. columns_[14] = "hwaddr_source";
  647. columns_[15] = "state";
  648. BOOST_STATIC_ASSERT(15 < LEASE_COLUMNS);
  649. }
  650. /// @brief Create MYSQL_BIND objects for Lease6 Pointer
  651. ///
  652. /// Fills in the MYSQL_BIND array for sending data in the Lease4 object to
  653. /// the database.
  654. ///
  655. /// @param lease Lease object to be added to the database.
  656. ///
  657. /// @return Vector of MySQL BIND objects representing the data to be added.
  658. std::vector<MYSQL_BIND> createBindForSend(const Lease6Ptr& lease) {
  659. // Store lease object to ensure it remains valid.
  660. lease_ = lease;
  661. // Ensure bind_ array clear for constructing the MYSQL_BIND structures
  662. // for this lease.
  663. // It sets all fields, including is_null, to zero, so we need to set
  664. // is_null only if it should be true. This gives up minor performance
  665. // benefit while being safe approach. For improved readability, the
  666. // code that explicitly sets is_null is there, but is commented out.
  667. memset(bind_, 0, sizeof(bind_));
  668. try {
  669. // address: varchar(39)
  670. addr6_ = lease_->addr_.toText();
  671. addr6_length_ = addr6_.size();
  672. // In the following statement, the string is being read. However, the
  673. // MySQL C interface does not use "const", so the "buffer" element
  674. // is declared as "char*" instead of "const char*". To resolve this,
  675. // the "const" is discarded. (Note that the address of addr6_.c_str()
  676. // is guaranteed to be valid until the next non-const operation on
  677. // addr6_.)
  678. //
  679. // The const_cast could be avoided by copying the string to a writeable
  680. // buffer and storing the address of that in the "buffer" element.
  681. // However, this introduces a copy operation (with additional overhead)
  682. // purely to get round the structures introduced by design of the
  683. // MySQL interface (which uses the area pointed to by "buffer" as input
  684. // when specifying query parameters and as output when retrieving data).
  685. // For that reason, "const_cast" has been used.
  686. bind_[0].buffer_type = MYSQL_TYPE_STRING;
  687. bind_[0].buffer = const_cast<char*>(addr6_.c_str());
  688. bind_[0].buffer_length = addr6_length_;
  689. bind_[0].length = &addr6_length_;
  690. // bind_[0].is_null = &MLM_FALSE; // commented out for performance
  691. // reasons, see memset() above
  692. // duid: varchar(128)
  693. if (!lease_->duid_) {
  694. isc_throw(DbOperationError, "lease6 for address " << addr6_
  695. << " is missing mandatory client-id.");
  696. }
  697. duid_ = lease_->duid_->getDuid();
  698. duid_length_ = duid_.size();
  699. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  700. bind_[1].buffer = reinterpret_cast<char*>(&(duid_[0]));
  701. bind_[1].buffer_length = duid_length_;
  702. bind_[1].length = &duid_length_;
  703. // bind_[1].is_null = &MLM_FALSE; // commented out for performance
  704. // reasons, see memset() above
  705. // valid lifetime: unsigned int
  706. bind_[2].buffer_type = MYSQL_TYPE_LONG;
  707. bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
  708. bind_[2].is_unsigned = MLM_TRUE;
  709. // bind_[2].is_null = &MLM_FALSE; // commented out for performance
  710. // reasons, see memset() above
  711. // expire: timestamp
  712. // The lease structure holds the client last transmission time (cltt_)
  713. // For convenience for external tools, this is converted to lease
  714. /// expiry time (expire). The relationship is given by:
  715. //
  716. // expire = cltt_ + valid_lft_
  717. //
  718. MySqlLeaseMgr::convertToDatabaseTime(lease_->cltt_, lease_->valid_lft_,
  719. expire_);
  720. bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
  721. bind_[3].buffer = reinterpret_cast<char*>(&expire_);
  722. bind_[3].buffer_length = sizeof(expire_);
  723. // bind_[3].is_null = &MLM_FALSE; // commented out for performance
  724. // reasons, see memset() above
  725. // subnet_id: unsigned int
  726. // Can use lease_->subnet_id_ directly as it is of type uint32_t.
  727. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  728. bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
  729. bind_[4].is_unsigned = MLM_TRUE;
  730. // bind_[4].is_null = &MLM_FALSE; // commented out for performance
  731. // reasons, see memset() above
  732. // pref_lifetime: unsigned int
  733. // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
  734. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  735. bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
  736. bind_[5].is_unsigned = MLM_TRUE;
  737. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  738. // reasons, see memset() above
  739. // lease_type: tinyint
  740. // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
  741. lease_type_ = lease_->type_;
  742. bind_[6].buffer_type = MYSQL_TYPE_TINY;
  743. bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
  744. bind_[6].is_unsigned = MLM_TRUE;
  745. // bind_[6].is_null = &MLM_FALSE; // commented out for performance
  746. // reasons, see memset() above
  747. // iaid: unsigned int
  748. // Can use lease_->iaid_ directly as it is of type uint32_t.
  749. bind_[7].buffer_type = MYSQL_TYPE_LONG;
  750. bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
  751. bind_[7].is_unsigned = MLM_TRUE;
  752. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  753. // reasons, see memset() above
  754. // prefix_len: unsigned tinyint
  755. // Can use lease_->prefixlen_ directly as it is uint32_t.
  756. bind_[8].buffer_type = MYSQL_TYPE_TINY;
  757. bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
  758. bind_[8].is_unsigned = MLM_TRUE;
  759. // bind_[8].is_null = &MLM_FALSE; // commented out for performance
  760. // reasons, see memset() above
  761. // fqdn_fwd: boolean
  762. bind_[9].buffer_type = MYSQL_TYPE_TINY;
  763. bind_[9].buffer = reinterpret_cast<char*>(&lease_->fqdn_fwd_);
  764. bind_[9].is_unsigned = MLM_TRUE;
  765. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  766. // reasons, see memset() above
  767. // fqdn_rev: boolean
  768. bind_[10].buffer_type = MYSQL_TYPE_TINY;
  769. bind_[10].buffer = reinterpret_cast<char*>(&lease_->fqdn_rev_);
  770. bind_[10].is_unsigned = MLM_TRUE;
  771. // bind_[10].is_null = &MLM_FALSE; // commented out for performance
  772. // reasons, see memset() above
  773. // hostname: varchar(255)
  774. bind_[11].buffer_type = MYSQL_TYPE_VARCHAR;
  775. bind_[11].buffer = const_cast<char*>(lease_->hostname_.c_str());
  776. bind_[11].buffer_length = lease_->hostname_.length();
  777. // bind_[11].is_null = &MLM_FALSE; // commented out for performance
  778. // reasons, see memset() above
  779. // hwaddr: varbinary(20) - hardware/MAC address
  780. HWAddrPtr hwaddr = lease_->hwaddr_;
  781. if (hwaddr) {
  782. hwaddr_ = hwaddr->hwaddr_;
  783. hwaddr_length_ = hwaddr->hwaddr_.size();
  784. bind_[12].buffer_type = MYSQL_TYPE_BLOB;
  785. bind_[12].buffer = reinterpret_cast<char*>(&(hwaddr_[0]));
  786. bind_[12].buffer_length = hwaddr_length_;
  787. bind_[12].length = &hwaddr_length_;
  788. } else {
  789. bind_[12].buffer_type = MYSQL_TYPE_NULL;
  790. // According to http://dev.mysql.com/doc/refman/5.5/en/
  791. // c-api-prepared-statement-data-structures.html, the other
  792. // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
  793. // but let's set them to some sane values in case earlier versions
  794. // didn't have that assumption.
  795. hwaddr_null_ = MLM_TRUE;
  796. bind_[12].buffer = NULL;
  797. bind_[12].is_null = &hwaddr_null_;
  798. }
  799. // hwtype
  800. if (hwaddr) {
  801. hwtype_ = lease->hwaddr_->htype_;
  802. bind_[13].buffer_type = MYSQL_TYPE_SHORT;
  803. bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
  804. bind_[13].is_unsigned = MLM_TRUE;
  805. } else {
  806. hwtype_ = 0;
  807. bind_[13].buffer_type = MYSQL_TYPE_NULL;
  808. // According to http://dev.mysql.com/doc/refman/5.5/en/
  809. // c-api-prepared-statement-data-structures.html, the other
  810. // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
  811. // but let's set them to some sane values in case earlier versions
  812. // didn't have that assumption.
  813. hwaddr_null_ = MLM_TRUE;
  814. bind_[13].buffer = NULL;
  815. bind_[13].is_null = &hwaddr_null_;
  816. }
  817. /// Hardware source
  818. if (hwaddr) {
  819. hwaddr_source_ = lease->hwaddr_->source_;
  820. bind_[14].buffer_type = MYSQL_TYPE_LONG;
  821. bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
  822. bind_[14].is_unsigned = MLM_TRUE;
  823. } else {
  824. hwaddr_source_ = 0;
  825. bind_[14].buffer_type = MYSQL_TYPE_NULL;
  826. // According to http://dev.mysql.com/doc/refman/5.5/en/
  827. // c-api-prepared-statement-data-structures.html, the other
  828. // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
  829. // but let's set them to some sane values in case earlier versions
  830. // didn't have that assumption.
  831. hwaddr_null_ = MLM_TRUE;
  832. bind_[14].buffer = NULL;
  833. bind_[14].is_null = &hwaddr_null_;
  834. }
  835. // state: uint32_t
  836. bind_[15].buffer_type = MYSQL_TYPE_LONG;
  837. bind_[15].buffer = reinterpret_cast<char*>(&lease_->state_);
  838. bind_[15].is_unsigned = MLM_TRUE;
  839. // bind_[15].is_null = &MLM_FALSE; // commented out for performance
  840. // reasons, see memset() above
  841. // Add the error flags
  842. setErrorIndicators(bind_, error_, LEASE_COLUMNS);
  843. // .. and check that we have the numbers correct at compile time.
  844. BOOST_STATIC_ASSERT(14 < LEASE_COLUMNS);
  845. } catch (const std::exception& ex) {
  846. isc_throw(DbOperationError,
  847. "Could not create bind array from Lease6: "
  848. << lease_->addr_.toText() << ", reason: " << ex.what());
  849. }
  850. // Add the data to the vector. Note the end element is one after the
  851. // end of the array.
  852. return (std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
  853. }
  854. /// @brief Create BIND array to receive data
  855. ///
  856. /// Creates a MYSQL_BIND array to receive Lease6 data from the database.
  857. /// After data is successfully received, getLeaseData() is used to copy
  858. /// it to a Lease6 object.
  859. ///
  860. /// @return Vector of MySQL BIND objects passed to the MySQL data retrieval
  861. /// functions.
  862. std::vector<MYSQL_BIND> createBindForReceive() {
  863. // Initialize MYSQL_BIND array.
  864. // It sets all fields, including is_null, to zero, so we need to set
  865. // is_null only if it should be true. This gives up minor performance
  866. // benefit while being safe approach. For improved readability, the
  867. // code that explicitly sets is_null is there, but is commented out.
  868. memset(bind_, 0, sizeof(bind_));
  869. // address: varchar(39)
  870. // A Lease6_ address has a maximum of 39 characters. The array is
  871. // one byte longer than this to guarantee that we can always null
  872. // terminate it whatever is returned.
  873. addr6_length_ = sizeof(addr6_buffer_) - 1;
  874. bind_[0].buffer_type = MYSQL_TYPE_STRING;
  875. bind_[0].buffer = addr6_buffer_;
  876. bind_[0].buffer_length = addr6_length_;
  877. bind_[0].length = &addr6_length_;
  878. // bind_[0].is_null = &MLM_FALSE; // commented out for performance
  879. // reasons, see memset() above
  880. // client_id: varbinary(128)
  881. duid_length_ = sizeof(duid_buffer_);
  882. bind_[1].buffer_type = MYSQL_TYPE_BLOB;
  883. bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
  884. bind_[1].buffer_length = duid_length_;
  885. bind_[1].length = &duid_length_;
  886. // bind_[1].is_null = &MLM_FALSE; // commented out for performance
  887. // reasons, see memset() above
  888. // lease_time: unsigned int
  889. bind_[2].buffer_type = MYSQL_TYPE_LONG;
  890. bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
  891. bind_[2].is_unsigned = MLM_TRUE;
  892. // bind_[2].is_null = &MLM_FALSE; // commented out for performance
  893. // reasons, see memset() above
  894. // expire: timestamp
  895. bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
  896. bind_[3].buffer = reinterpret_cast<char*>(&expire_);
  897. bind_[3].buffer_length = sizeof(expire_);
  898. // bind_[3].is_null = &MLM_FALSE; // commented out for performance
  899. // reasons, see memset() above
  900. // subnet_id: unsigned int
  901. bind_[4].buffer_type = MYSQL_TYPE_LONG;
  902. bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
  903. bind_[4].is_unsigned = MLM_TRUE;
  904. // bind_[4].is_null = &MLM_FALSE; // commented out for performance
  905. // reasons, see memset() above
  906. // pref_lifetime: unsigned int
  907. bind_[5].buffer_type = MYSQL_TYPE_LONG;
  908. bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
  909. bind_[5].is_unsigned = MLM_TRUE;
  910. // bind_[5].is_null = &MLM_FALSE; // commented out for performance
  911. // reasons, see memset() above
  912. // lease_type: tinyint
  913. bind_[6].buffer_type = MYSQL_TYPE_TINY;
  914. bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
  915. bind_[6].is_unsigned = MLM_TRUE;
  916. // bind_[6].is_null = &MLM_FALSE; // commented out for performance
  917. // reasons, see memset() above
  918. // iaid: unsigned int
  919. bind_[7].buffer_type = MYSQL_TYPE_LONG;
  920. bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
  921. bind_[7].is_unsigned = MLM_TRUE;
  922. // bind_[7].is_null = &MLM_FALSE; // commented out for performance
  923. // reasons, see memset() above
  924. // prefix_len: unsigned tinyint
  925. bind_[8].buffer_type = MYSQL_TYPE_TINY;
  926. bind_[8].buffer = reinterpret_cast<char*>(&prefixlen_);
  927. bind_[8].is_unsigned = MLM_TRUE;
  928. // bind_[8].is_null = &MLM_FALSE; // commented out for performance
  929. // reasons, see memset() above
  930. // fqdn_fwd: boolean
  931. bind_[9].buffer_type = MYSQL_TYPE_TINY;
  932. bind_[9].buffer = reinterpret_cast<char*>(&fqdn_fwd_);
  933. bind_[9].is_unsigned = MLM_TRUE;
  934. // bind_[9].is_null = &MLM_FALSE; // commented out for performance
  935. // reasons, see memset() above
  936. // fqdn_rev: boolean
  937. bind_[10].buffer_type = MYSQL_TYPE_TINY;
  938. bind_[10].buffer = reinterpret_cast<char*>(&fqdn_rev_);
  939. bind_[10].is_unsigned = MLM_TRUE;
  940. // bind_[10].is_null = &MLM_FALSE; // commented out for performance
  941. // reasons, see memset() above
  942. // hostname: varchar(255)
  943. hostname_length_ = sizeof(hostname_buffer_);
  944. bind_[11].buffer_type = MYSQL_TYPE_STRING;
  945. bind_[11].buffer = reinterpret_cast<char*>(hostname_buffer_);
  946. bind_[11].buffer_length = hostname_length_;
  947. bind_[11].length = &hostname_length_;
  948. // bind_[11].is_null = &MLM_FALSE; // commented out for performance
  949. // reasons, see memset() above
  950. // hardware address
  951. // hwaddr: varbinary(20)
  952. hwaddr_null_ = MLM_FALSE;
  953. hwaddr_length_ = sizeof(hwaddr_buffer_);
  954. bind_[12].buffer_type = MYSQL_TYPE_BLOB;
  955. bind_[12].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
  956. bind_[12].buffer_length = hwaddr_length_;
  957. bind_[12].length = &hwaddr_length_;
  958. bind_[12].is_null = &hwaddr_null_;
  959. // hardware type: unsigned short int (16 bits)
  960. bind_[13].buffer_type = MYSQL_TYPE_SHORT;
  961. bind_[13].buffer = reinterpret_cast<char*>(&hwtype_);
  962. bind_[13].is_unsigned = MLM_TRUE;
  963. // hardware source: unsigned int (32 bits)
  964. bind_[14].buffer_type = MYSQL_TYPE_LONG;
  965. bind_[14].buffer = reinterpret_cast<char*>(&hwaddr_source_);
  966. bind_[14].is_unsigned = MLM_TRUE;
  967. // state: uint32_t
  968. bind_[15].buffer_type = MYSQL_TYPE_LONG;
  969. bind_[15].buffer = reinterpret_cast<char*>(&state_);
  970. bind_[15].is_unsigned = MLM_TRUE;
  971. // bind_[15].is_null = &MLM_FALSE; // commented out for performance
  972. // reasons, see memset() above
  973. // Add the error flags
  974. setErrorIndicators(bind_, error_, LEASE_COLUMNS);
  975. // .. and check that we have the numbers correct at compile time.
  976. BOOST_STATIC_ASSERT(15 < LEASE_COLUMNS);
  977. // Add the data to the vector. Note the end element is one after the
  978. // end of the array.
  979. return(std::vector<MYSQL_BIND>(&bind_[0], &bind_[LEASE_COLUMNS]));
  980. }
  981. /// @brief Copy Received Data into Lease6 Object
  982. ///
  983. /// Called after the MYSQL_BIND array created by createBindForReceive()
  984. /// has been used, this copies data from the internal member variables
  985. /// into a Lease6 object.
  986. ///
  987. /// @return Lease6Ptr Pointer to a Lease6 object holding the relevant
  988. /// data.
  989. ///
  990. /// @throw isc::BadValue Unable to convert Lease Type value in database
  991. Lease6Ptr getLeaseData() {
  992. // The address buffer is declared larger than the buffer size passed
  993. // to the access function so that we can always append a null byte.
  994. // Create the IOAddress object corresponding to the received data.
  995. addr6_buffer_[addr6_length_] = '\0';
  996. std::string address = addr6_buffer_;
  997. isc::asiolink::IOAddress addr(address);
  998. // Set the lease type in a variable of the appropriate data type, which
  999. // has been initialized with an arbitrary (but valid) value.
  1000. Lease::Type type = Lease::TYPE_NA;
  1001. switch (lease_type_) {
  1002. case Lease::TYPE_NA:
  1003. type = Lease::TYPE_NA;
  1004. break;
  1005. case Lease::TYPE_TA:
  1006. type = Lease::TYPE_TA;
  1007. break;
  1008. case Lease::TYPE_PD:
  1009. type = Lease::TYPE_PD;
  1010. break;
  1011. default:
  1012. isc_throw(BadValue, "invalid lease type returned (" <<
  1013. static_cast<int>(lease_type_) << ") for lease with "
  1014. << "address " << address << ". Only 0, 1, or 2 are "
  1015. << "allowed.");
  1016. }
  1017. // Set up DUID,
  1018. DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
  1019. // Hostname is passed to Lease6 as a string object, so we have to
  1020. // create it from the hostname buffer and length.
  1021. std::string hostname(hostname_buffer_,
  1022. hostname_buffer_ + hostname_length_);
  1023. /// Set hardware address if it was set
  1024. HWAddrPtr hwaddr;
  1025. if (hwaddr_null_ == MLM_FALSE) {
  1026. hwaddr.reset(new HWAddr(hwaddr_buffer_, hwaddr_length_, hwtype_));
  1027. hwaddr->source_ = hwaddr_source_;
  1028. }
  1029. // Create the lease and set the cltt (after converting from the
  1030. // expire time retrieved from the database).
  1031. Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
  1032. pref_lifetime_, valid_lifetime_, 0, 0,
  1033. subnet_id_, fqdn_fwd_, fqdn_rev_,
  1034. hostname, hwaddr, prefixlen_));
  1035. time_t cltt = 0;
  1036. MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
  1037. result->cltt_ = cltt;
  1038. // Set state.
  1039. result->state_ = state_;
  1040. return (result);
  1041. }
  1042. /// @brief Return columns in error
  1043. ///
  1044. /// If an error is returned from a fetch (in particular, a truncated
  1045. /// status), this method can be called to get the names of the fields in
  1046. /// error. It returns a string comprising the names of the fields
  1047. /// separated by commas. In the case of there being no error indicators
  1048. /// set, it returns the string "(None)".
  1049. ///
  1050. /// @return Comma-separated list of columns in error, or the string
  1051. /// "(None)".
  1052. std::string getErrorColumns() {
  1053. return (getColumnsInError(error_, columns_, LEASE_COLUMNS));
  1054. }
  1055. private:
  1056. // Note: All array lengths are equal to the corresponding variable in the
  1057. // schema.
  1058. // Note: arrays are declared fixed length for speed of creation
  1059. std::string addr6_; ///< String form of address
  1060. char addr6_buffer_[ADDRESS6_TEXT_MAX_LEN + 1]; ///< Character
  1061. ///< array form of V6 address
  1062. unsigned long addr6_length_; ///< Length of the address
  1063. MYSQL_BIND bind_[LEASE_COLUMNS]; ///< Bind array
  1064. std::string columns_[LEASE_COLUMNS];///< Column names
  1065. std::vector<uint8_t> duid_; ///< Client identification
  1066. uint8_t duid_buffer_[DUID::MAX_DUID_LEN]; ///< Buffer form of DUID
  1067. unsigned long duid_length_; ///< Length of the DUID
  1068. my_bool error_[LEASE_COLUMNS]; ///< Error indicators
  1069. MYSQL_TIME expire_; ///< Lease expiry time
  1070. uint32_t iaid_; ///< Identity association ID
  1071. Lease6Ptr lease_; ///< Pointer to lease object
  1072. uint8_t lease_type_; ///< Lease type
  1073. uint8_t prefixlen_; ///< Prefix length
  1074. uint32_t pref_lifetime_; ///< Preferred lifetime
  1075. uint32_t subnet_id_; ///< Subnet identification
  1076. uint32_t valid_lifetime_; ///< Lease time
  1077. my_bool fqdn_fwd_; ///< Has forward DNS update been
  1078. ///< performed
  1079. my_bool fqdn_rev_; ///< Has reverse DNS update been
  1080. ///< performed
  1081. char hostname_buffer_[HOSTNAME_MAX_LEN];
  1082. ///< Client hostname
  1083. unsigned long hostname_length_; ///< Client hostname length
  1084. uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
  1085. ///< Buffer for Hardware address
  1086. std::vector<uint8_t> hwaddr_; ///< Hardware address (optional)
  1087. unsigned long hwaddr_length_; ///< Aux. variable denoting hwaddr_ size()
  1088. my_bool hwaddr_null_; ///< Used when HWAddr is null
  1089. uint16_t hwtype_; ///< Hardware type
  1090. uint32_t hwaddr_source_; ///< Source of the hardware address
  1091. uint32_t state_; ///< Lease state.
  1092. };
  1093. // MySqlLeaseMgr Constructor and Destructor
  1094. MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
  1095. : conn_(parameters) {
  1096. // Open the database.
  1097. conn_.openDatabase();
  1098. // Enable autocommit. To avoid a flush to disk on every commit, the global
  1099. // parameter innodb_flush_log_at_trx_commit should be set to 2. This will
  1100. // cause the changes to be written to the log, but flushed to disk in the
  1101. // background every second. Setting the parameter to that value will speed
  1102. // up the system, but at the risk of losing data if the system crashes.
  1103. my_bool result = mysql_autocommit(conn_.mysql_, 1);
  1104. if (result != 0) {
  1105. isc_throw(DbOperationError, mysql_error(conn_.mysql_));
  1106. }
  1107. // Prepare all statements likely to be used.
  1108. conn_.prepareStatements(tagged_statements, MySqlLeaseMgr::NUM_STATEMENTS);
  1109. // Create the exchange objects for use in exchanging data between the
  1110. // program and the database.
  1111. exchange4_.reset(new MySqlLease4Exchange());
  1112. exchange6_.reset(new MySqlLease6Exchange());
  1113. }
  1114. MySqlLeaseMgr::~MySqlLeaseMgr() {
  1115. // There is no need to close the database in this destructor: it is
  1116. // closed in the destructor of the mysql_ member variable.
  1117. }
  1118. std::string
  1119. MySqlLeaseMgr::getDBVersion() {
  1120. std::stringstream tmp;
  1121. tmp << "MySQL backend " << CURRENT_VERSION_VERSION;
  1122. tmp << "." << CURRENT_VERSION_MINOR;
  1123. tmp << ", library " << mysql_get_client_info();
  1124. return (tmp.str());
  1125. }
  1126. // Time conversion methods.
  1127. //
  1128. // Note that the MySQL TIMESTAMP data type (used for "expire") converts data
  1129. // from the current timezone to UTC for storage, and from UTC to the current
  1130. // timezone for retrieval.
  1131. //
  1132. // This causes no problems providing that:
  1133. // a) cltt is given in local time
  1134. // b) We let the system take care of timezone conversion when converting
  1135. // from a time read from the database into a local time.
  1136. void
  1137. MySqlLeaseMgr::convertToDatabaseTime(const time_t input_time,
  1138. MYSQL_TIME& output_time) {
  1139. // Convert to broken-out time
  1140. struct tm time_tm;
  1141. (void) localtime_r(&input_time, &time_tm);
  1142. // Place in output expire structure.
  1143. output_time.year = time_tm.tm_year + 1900;
  1144. output_time.month = time_tm.tm_mon + 1; // Note different base
  1145. output_time.day = time_tm.tm_mday;
  1146. output_time.hour = time_tm.tm_hour;
  1147. output_time.minute = time_tm.tm_min;
  1148. output_time.second = time_tm.tm_sec;
  1149. output_time.second_part = 0; // No fractional seconds
  1150. output_time.neg = my_bool(0); // Not negative
  1151. }
  1152. void
  1153. MySqlLeaseMgr::convertToDatabaseTime(const time_t cltt,
  1154. const uint32_t valid_lifetime,
  1155. MYSQL_TIME& expire) {
  1156. // Calculate expiry time. Store it in the 64-bit value so as we can detect
  1157. // overflows.
  1158. const int64_t expire_time_64 = static_cast<int64_t>(cltt) +
  1159. static_cast<int64_t>(valid_lifetime);
  1160. // Even on 64-bit systems MySQL doesn't seem to accept the timestamps
  1161. // beyond the max value of int32_t.
  1162. if (expire_time_64 > LeaseMgr::MAX_DB_TIME) {
  1163. isc_throw(BadValue, "Time value is too large: " << expire_time_64);
  1164. }
  1165. convertToDatabaseTime(static_cast<time_t>(expire_time_64), expire);
  1166. }
  1167. void
  1168. MySqlLeaseMgr::convertFromDatabaseTime(const MYSQL_TIME& expire,
  1169. uint32_t valid_lifetime, time_t& cltt) {
  1170. // Copy across fields from MYSQL_TIME structure.
  1171. struct tm expire_tm;
  1172. memset(&expire_tm, 0, sizeof(expire_tm));
  1173. expire_tm.tm_year = expire.year - 1900;
  1174. expire_tm.tm_mon = expire.month - 1;
  1175. expire_tm.tm_mday = expire.day;
  1176. expire_tm.tm_hour = expire.hour;
  1177. expire_tm.tm_min = expire.minute;
  1178. expire_tm.tm_sec = expire.second;
  1179. expire_tm.tm_isdst = -1; // Let the system work out about DST
  1180. // Convert to local time
  1181. cltt = mktime(&expire_tm) - valid_lifetime;
  1182. }
  1183. // Add leases to the database. The two public methods accept a lease object
  1184. // (either V4 of V6), bind the contents to the appropriate prepared
  1185. // statement, then call common code to execute the statement.
  1186. bool
  1187. MySqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
  1188. std::vector<MYSQL_BIND>& bind) {
  1189. // Bind the parameters to the statement
  1190. int status = mysql_stmt_bind_param(conn_.statements_[stindex], &bind[0]);
  1191. checkError(status, stindex, "unable to bind parameters");
  1192. // Execute the statement
  1193. status = mysql_stmt_execute(conn_.statements_[stindex]);
  1194. if (status != 0) {
  1195. // Failure: check for the special case of duplicate entry. If this is
  1196. // the case, we return false to indicate that the row was not added.
  1197. // Otherwise we throw an exception.
  1198. if (mysql_errno(conn_.mysql_) == ER_DUP_ENTRY) {
  1199. return (false);
  1200. }
  1201. checkError(status, stindex, "unable to execute");
  1202. }
  1203. // Insert succeeded
  1204. return (true);
  1205. }
  1206. bool
  1207. MySqlLeaseMgr::addLease(const Lease4Ptr& lease) {
  1208. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1209. DHCPSRV_MYSQL_ADD_ADDR4).arg(lease->addr_.toText());
  1210. // Create the MYSQL_BIND array for the lease
  1211. std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
  1212. // ... and drop to common code.
  1213. return (addLeaseCommon(INSERT_LEASE4, bind));
  1214. }
  1215. bool
  1216. MySqlLeaseMgr::addLease(const Lease6Ptr& lease) {
  1217. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1218. DHCPSRV_MYSQL_ADD_ADDR6).arg(lease->addr_.toText())
  1219. .arg(lease->type_);
  1220. // Create the MYSQL_BIND array for the lease
  1221. std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
  1222. // ... and drop to common code.
  1223. return (addLeaseCommon(INSERT_LEASE6, bind));
  1224. }
  1225. // Extraction of leases from the database.
  1226. //
  1227. // All getLease() methods ultimately call getLeaseCollection(). This
  1228. // binds the input parameters passed to it with the appropriate prepared
  1229. // statement and executes the statement. It then gets the results from the
  1230. // database. getlease() methods that expect a single result back call it
  1231. // with the "single" parameter set true: this causes an exception to be
  1232. // generated if multiple records can be retrieved from the result set. (Such
  1233. // an occurrence either indicates corruption in the database, or that an
  1234. // assumption that a query can only return a single record is incorrect.)
  1235. // Methods that require a collection of records have "single" set to the
  1236. // default value of false. The logic is the same for both Lease4 and Lease6
  1237. // objects, so the code is templated.
  1238. //
  1239. // Methods that require a collection of objects access this method through
  1240. // two interface methods (also called getLeaseCollection()). These are
  1241. // short enough as to be defined in the header file: all they do is to supply
  1242. // the appropriate MySqlLeaseXExchange object depending on the type of the
  1243. // LeaseCollection objects passed to them.
  1244. //
  1245. // Methods that require a single object to be returned access the method
  1246. // through two interface methods (called getLease()). As well as supplying
  1247. // the appropriate exchange object, they convert between lease collection
  1248. // holding zero or one leases into an appropriate Lease object.
  1249. template <typename Exchange, typename LeaseCollection>
  1250. void MySqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
  1251. MYSQL_BIND* bind,
  1252. Exchange& exchange,
  1253. LeaseCollection& result,
  1254. bool single) const {
  1255. // Bind the selection parameters to the statement
  1256. int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
  1257. checkError(status, stindex, "unable to bind WHERE clause parameter");
  1258. // Set up the MYSQL_BIND array for the data being returned and bind it to
  1259. // the statement.
  1260. std::vector<MYSQL_BIND> outbind = exchange->createBindForReceive();
  1261. status = mysql_stmt_bind_result(conn_.statements_[stindex], &outbind[0]);
  1262. checkError(status, stindex, "unable to bind SELECT clause parameters");
  1263. // Execute the statement
  1264. status = mysql_stmt_execute(conn_.statements_[stindex]);
  1265. checkError(status, stindex, "unable to execute");
  1266. // Ensure that all the lease information is retrieved in one go to avoid
  1267. // overhead of going back and forth between client and server.
  1268. status = mysql_stmt_store_result(conn_.statements_[stindex]);
  1269. checkError(status, stindex, "unable to set up for storing all results");
  1270. // Set up the fetch "release" object to release resources associated
  1271. // with the call to mysql_stmt_fetch when this method exits, then
  1272. // retrieve the data.
  1273. MySqlFreeResult fetch_release(conn_.statements_[stindex]);
  1274. int count = 0;
  1275. while ((status = mysql_stmt_fetch(conn_.statements_[stindex])) == 0) {
  1276. try {
  1277. result.push_back(exchange->getLeaseData());
  1278. } catch (const isc::BadValue& ex) {
  1279. // Rethrow the exception with a bit more data.
  1280. isc_throw(BadValue, ex.what() << ". Statement is <" <<
  1281. conn_.text_statements_[stindex] << ">");
  1282. }
  1283. if (single && (++count > 1)) {
  1284. isc_throw(MultipleRecords, "multiple records were found in the "
  1285. "database where only one was expected for query "
  1286. << conn_.text_statements_[stindex]);
  1287. }
  1288. }
  1289. // How did the fetch end?
  1290. if (status == 1) {
  1291. // Error - unable to fetch results
  1292. checkError(status, stindex, "unable to fetch results");
  1293. } else if (status == MYSQL_DATA_TRUNCATED) {
  1294. // Data truncated - throw an exception indicating what was at fault
  1295. isc_throw(DataTruncated, conn_.text_statements_[stindex]
  1296. << " returned truncated data: columns affected are "
  1297. << exchange->getErrorColumns());
  1298. }
  1299. }
  1300. void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
  1301. Lease4Ptr& result) const {
  1302. // Create appropriate collection object and get all leases matching
  1303. // the selection criteria. The "single" paraeter is true to indicate
  1304. // that the called method should throw an exception if multiple
  1305. // matching records are found: this particular method is called when only
  1306. // one or zero matches is expected.
  1307. Lease4Collection collection;
  1308. getLeaseCollection(stindex, bind, exchange4_, collection, true);
  1309. // Return single record if present, else clear the lease.
  1310. if (collection.empty()) {
  1311. result.reset();
  1312. } else {
  1313. result = *collection.begin();
  1314. }
  1315. }
  1316. void MySqlLeaseMgr::getLease(StatementIndex stindex, MYSQL_BIND* bind,
  1317. Lease6Ptr& result) const {
  1318. // Create appropriate collection object and get all leases matching
  1319. // the selection criteria. The "single" paraeter is true to indicate
  1320. // that the called method should throw an exception if multiple
  1321. // matching records are found: this particular method is called when only
  1322. // one or zero matches is expected.
  1323. Lease6Collection collection;
  1324. getLeaseCollection(stindex, bind, exchange6_, collection, true);
  1325. // Return single record if present, else clear the lease.
  1326. if (collection.empty()) {
  1327. result.reset();
  1328. } else {
  1329. result = *collection.begin();
  1330. }
  1331. }
  1332. // Basic lease access methods. Obtain leases from the database using various
  1333. // criteria.
  1334. Lease4Ptr
  1335. MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
  1336. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1337. DHCPSRV_MYSQL_GET_ADDR4).arg(addr.toText());
  1338. // Set up the WHERE clause value
  1339. MYSQL_BIND inbind[1];
  1340. memset(inbind, 0, sizeof(inbind));
  1341. uint32_t addr4 = static_cast<uint32_t>(addr);
  1342. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1343. inbind[0].buffer = reinterpret_cast<char*>(&addr4);
  1344. inbind[0].is_unsigned = MLM_TRUE;
  1345. // Get the data
  1346. Lease4Ptr result;
  1347. getLease(GET_LEASE4_ADDR, inbind, result);
  1348. return (result);
  1349. }
  1350. Lease4Collection
  1351. MySqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
  1352. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1353. DHCPSRV_MYSQL_GET_HWADDR).arg(hwaddr.toText());
  1354. // Set up the WHERE clause value
  1355. MYSQL_BIND inbind[1];
  1356. memset(inbind, 0, sizeof(inbind));
  1357. // As "buffer" is "char*" - even though the data is being read - we need
  1358. // to cast away the "const"ness as well as reinterpreting the data as
  1359. // a "char*". (We could avoid the "const_cast" by copying the data to a
  1360. // local variable, but as the data is only being read, this introduces
  1361. // an unnecessary copy).
  1362. unsigned long hwaddr_length = hwaddr.hwaddr_.size();
  1363. uint8_t* data = const_cast<uint8_t*>(&hwaddr.hwaddr_[0]);
  1364. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1365. inbind[0].buffer = reinterpret_cast<char*>(data);
  1366. inbind[0].buffer_length = hwaddr_length;
  1367. inbind[0].length = &hwaddr_length;
  1368. // Get the data
  1369. Lease4Collection result;
  1370. getLeaseCollection(GET_LEASE4_HWADDR, inbind, result);
  1371. return (result);
  1372. }
  1373. Lease4Ptr
  1374. MySqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
  1375. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1376. DHCPSRV_MYSQL_GET_SUBID_HWADDR)
  1377. .arg(subnet_id).arg(hwaddr.toText());
  1378. // Set up the WHERE clause value
  1379. MYSQL_BIND inbind[2];
  1380. memset(inbind, 0, sizeof(inbind));
  1381. // As "buffer" is "char*" - even though the data is being read - we need
  1382. // to cast away the "const"ness as well as reinterpreting the data as
  1383. // a "char*". (We could avoid the "const_cast" by copying the data to a
  1384. // local variable, but as the data is only being read, this introduces
  1385. // an unnecessary copy).
  1386. unsigned long hwaddr_length = hwaddr.hwaddr_.size();
  1387. uint8_t* data = const_cast<uint8_t*>(&hwaddr.hwaddr_[0]);
  1388. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1389. inbind[0].buffer = reinterpret_cast<char*>(data);
  1390. inbind[0].buffer_length = hwaddr_length;
  1391. inbind[0].length = &hwaddr_length;
  1392. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1393. inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
  1394. inbind[1].is_unsigned = MLM_TRUE;
  1395. // Get the data
  1396. Lease4Ptr result;
  1397. getLease(GET_LEASE4_HWADDR_SUBID, inbind, result);
  1398. return (result);
  1399. }
  1400. Lease4Collection
  1401. MySqlLeaseMgr::getLease4(const ClientId& clientid) const {
  1402. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1403. DHCPSRV_MYSQL_GET_CLIENTID).arg(clientid.toText());
  1404. // Set up the WHERE clause value
  1405. MYSQL_BIND inbind[1];
  1406. memset(inbind, 0, sizeof(inbind));
  1407. std::vector<uint8_t> client_data = clientid.getClientId();
  1408. unsigned long client_data_length = client_data.size();
  1409. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1410. inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
  1411. inbind[0].buffer_length = client_data_length;
  1412. inbind[0].length = &client_data_length;
  1413. // Get the data
  1414. Lease4Collection result;
  1415. getLeaseCollection(GET_LEASE4_CLIENTID, inbind, result);
  1416. return (result);
  1417. }
  1418. Lease4Ptr
  1419. MySqlLeaseMgr::getLease4(const ClientId&, const HWAddr&, SubnetID) const {
  1420. /// This function is currently not implemented because allocation engine
  1421. /// searches for the lease using HW address or client identifier.
  1422. /// It never uses both parameters in the same time. We need to
  1423. /// consider if this function is needed at all.
  1424. isc_throw(NotImplemented, "The MySqlLeaseMgr::getLease4 function was"
  1425. " called, but it is not implemented");
  1426. }
  1427. Lease4Ptr
  1428. MySqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
  1429. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1430. DHCPSRV_MYSQL_GET_SUBID_CLIENTID)
  1431. .arg(subnet_id).arg(clientid.toText());
  1432. // Set up the WHERE clause value
  1433. MYSQL_BIND inbind[2];
  1434. memset(inbind, 0, sizeof(inbind));
  1435. std::vector<uint8_t> client_data = clientid.getClientId();
  1436. unsigned long client_data_length = client_data.size();
  1437. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1438. inbind[0].buffer = reinterpret_cast<char*>(&client_data[0]);
  1439. inbind[0].buffer_length = client_data_length;
  1440. inbind[0].length = &client_data_length;
  1441. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1442. inbind[1].buffer = reinterpret_cast<char*>(&subnet_id);
  1443. inbind[1].is_unsigned = MLM_TRUE;
  1444. // Get the data
  1445. Lease4Ptr result;
  1446. getLease(GET_LEASE4_CLIENTID_SUBID, inbind, result);
  1447. return (result);
  1448. }
  1449. Lease6Ptr
  1450. MySqlLeaseMgr::getLease6(Lease::Type lease_type,
  1451. const isc::asiolink::IOAddress& addr) const {
  1452. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1453. DHCPSRV_MYSQL_GET_ADDR6).arg(addr.toText())
  1454. .arg(lease_type);
  1455. // Set up the WHERE clause value
  1456. MYSQL_BIND inbind[2];
  1457. memset(inbind, 0, sizeof(inbind));
  1458. std::string addr6 = addr.toText();
  1459. unsigned long addr6_length = addr6.size();
  1460. // See the earlier description of the use of "const_cast" when accessing
  1461. // the address for an explanation of the reason.
  1462. inbind[0].buffer_type = MYSQL_TYPE_STRING;
  1463. inbind[0].buffer = const_cast<char*>(addr6.c_str());
  1464. inbind[0].buffer_length = addr6_length;
  1465. inbind[0].length = &addr6_length;
  1466. // LEASE_TYPE
  1467. inbind[1].buffer_type = MYSQL_TYPE_TINY;
  1468. inbind[1].buffer = reinterpret_cast<char*>(&lease_type);
  1469. inbind[1].is_unsigned = MLM_TRUE;
  1470. Lease6Ptr result;
  1471. getLease(GET_LEASE6_ADDR, inbind, result);
  1472. return (result);
  1473. }
  1474. Lease6Collection
  1475. MySqlLeaseMgr::getLeases6(Lease::Type lease_type,
  1476. const DUID& duid, uint32_t iaid) const {
  1477. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1478. DHCPSRV_MYSQL_GET_IAID_DUID).arg(iaid).arg(duid.toText())
  1479. .arg(lease_type);
  1480. // Set up the WHERE clause value
  1481. MYSQL_BIND inbind[3];
  1482. memset(inbind, 0, sizeof(inbind));
  1483. // In the following statement, the DUID is being read. However, the
  1484. // MySQL C interface does not use "const", so the "buffer" element
  1485. // is declared as "char*" instead of "const char*". To resolve this,
  1486. // the "const" is discarded before the uint8_t* is cast to char*.
  1487. //
  1488. // Note that the const_cast could be avoided by copying the DUID to
  1489. // a writeable buffer and storing the address of that in the "buffer"
  1490. // element. However, this introduces a copy operation (with additional
  1491. // overhead) purely to get round the structures introduced by design of
  1492. // the MySQL interface (which uses the area pointed to by "buffer" as
  1493. // input when specifying query parameters and as output when retrieving
  1494. // data). For that reason, "const_cast" has been used.
  1495. const vector<uint8_t>& duid_vector = duid.getDuid();
  1496. unsigned long duid_length = duid_vector.size();
  1497. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1498. inbind[0].buffer = reinterpret_cast<char*>(
  1499. const_cast<uint8_t*>(&duid_vector[0]));
  1500. inbind[0].buffer_length = duid_length;
  1501. inbind[0].length = &duid_length;
  1502. // IAID
  1503. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1504. inbind[1].buffer = reinterpret_cast<char*>(&iaid);
  1505. inbind[1].is_unsigned = MLM_TRUE;
  1506. // LEASE_TYPE
  1507. inbind[2].buffer_type = MYSQL_TYPE_TINY;
  1508. inbind[2].buffer = reinterpret_cast<char*>(&lease_type);
  1509. inbind[2].is_unsigned = MLM_TRUE;
  1510. // ... and get the data
  1511. Lease6Collection result;
  1512. getLeaseCollection(GET_LEASE6_DUID_IAID, inbind, result);
  1513. return (result);
  1514. }
  1515. Lease6Collection
  1516. MySqlLeaseMgr::getLeases6(Lease::Type lease_type,
  1517. const DUID& duid, uint32_t iaid,
  1518. SubnetID subnet_id) const {
  1519. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1520. DHCPSRV_MYSQL_GET_IAID_SUBID_DUID)
  1521. .arg(iaid).arg(subnet_id).arg(duid.toText())
  1522. .arg(lease_type);
  1523. // Set up the WHERE clause value
  1524. MYSQL_BIND inbind[4];
  1525. memset(inbind, 0, sizeof(inbind));
  1526. // See the earlier description of the use of "const_cast" when accessing
  1527. // the DUID for an explanation of the reason.
  1528. const vector<uint8_t>& duid_vector = duid.getDuid();
  1529. unsigned long duid_length = duid_vector.size();
  1530. inbind[0].buffer_type = MYSQL_TYPE_BLOB;
  1531. inbind[0].buffer = reinterpret_cast<char*>(
  1532. const_cast<uint8_t*>(&duid_vector[0]));
  1533. inbind[0].buffer_length = duid_length;
  1534. inbind[0].length = &duid_length;
  1535. // IAID
  1536. inbind[1].buffer_type = MYSQL_TYPE_LONG;
  1537. inbind[1].buffer = reinterpret_cast<char*>(&iaid);
  1538. inbind[1].is_unsigned = MLM_TRUE;
  1539. // Subnet ID
  1540. inbind[2].buffer_type = MYSQL_TYPE_LONG;
  1541. inbind[2].buffer = reinterpret_cast<char*>(&subnet_id);
  1542. inbind[2].is_unsigned = MLM_TRUE;
  1543. // LEASE_TYPE
  1544. inbind[3].buffer_type = MYSQL_TYPE_TINY;
  1545. inbind[3].buffer = reinterpret_cast<char*>(&lease_type);
  1546. inbind[3].is_unsigned = MLM_TRUE;
  1547. // ... and get the data
  1548. Lease6Collection result;
  1549. getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, inbind, result);
  1550. return (result);
  1551. }
  1552. void
  1553. MySqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
  1554. const size_t max_leases) const {
  1555. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_EXPIRED6)
  1556. .arg(max_leases);
  1557. getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
  1558. }
  1559. void
  1560. MySqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
  1561. const size_t max_leases) const {
  1562. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_GET_EXPIRED4)
  1563. .arg(max_leases);
  1564. getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
  1565. }
  1566. template<typename LeaseCollection>
  1567. void
  1568. MySqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
  1569. const size_t max_leases,
  1570. StatementIndex statement_index) const {
  1571. // Set up the WHERE clause value
  1572. MYSQL_BIND inbind[3];
  1573. memset(inbind, 0, sizeof(inbind));
  1574. // Exclude reclaimed leases.
  1575. uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
  1576. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1577. inbind[0].buffer = reinterpret_cast<char*>(&state);
  1578. inbind[0].is_unsigned = MLM_TRUE;
  1579. // Expiration timestamp.
  1580. MYSQL_TIME expire_time;
  1581. convertToDatabaseTime(time(NULL), expire_time);
  1582. inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
  1583. inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
  1584. inbind[1].buffer_length = sizeof(expire_time);
  1585. // If the number of leases is 0, we will return all leases. This is
  1586. // achieved by setting the limit to a very high value.
  1587. uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
  1588. std::numeric_limits<uint32_t>::max();
  1589. inbind[2].buffer_type = MYSQL_TYPE_LONG;
  1590. inbind[2].buffer = reinterpret_cast<char*>(&limit);
  1591. inbind[2].is_unsigned = MLM_TRUE;
  1592. // Get the data
  1593. getLeaseCollection(statement_index, inbind, expired_leases);
  1594. }
  1595. // Update lease methods. These comprise common code that handles the actual
  1596. // update, and type-specific methods that set up the parameters for the prepared
  1597. // statement depending on the type of lease.
  1598. template <typename LeasePtr>
  1599. void
  1600. MySqlLeaseMgr::updateLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind,
  1601. const LeasePtr& lease) {
  1602. // Bind the parameters to the statement
  1603. int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
  1604. checkError(status, stindex, "unable to bind parameters");
  1605. // Execute
  1606. status = mysql_stmt_execute(conn_.statements_[stindex]);
  1607. checkError(status, stindex, "unable to execute");
  1608. // See how many rows were affected. The statement should only update a
  1609. // single row.
  1610. int affected_rows = mysql_stmt_affected_rows(conn_.statements_[stindex]);
  1611. if (affected_rows == 0) {
  1612. isc_throw(NoSuchLease, "unable to update lease for address " <<
  1613. lease->addr_ << " as it does not exist");
  1614. } else if (affected_rows > 1) {
  1615. // Should not happen - primary key constraint should only have selected
  1616. // one row.
  1617. isc_throw(DbOperationError, "apparently updated more than one lease "
  1618. "that had the address " << lease->addr_);
  1619. }
  1620. }
  1621. void
  1622. MySqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
  1623. const StatementIndex stindex = UPDATE_LEASE4;
  1624. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1625. DHCPSRV_MYSQL_UPDATE_ADDR4).arg(lease->addr_.toText());
  1626. // Create the MYSQL_BIND array for the data being updated
  1627. std::vector<MYSQL_BIND> bind = exchange4_->createBindForSend(lease);
  1628. // Set up the WHERE clause and append it to the MYSQL_BIND array
  1629. MYSQL_BIND where;
  1630. memset(&where, 0, sizeof(where));
  1631. uint32_t addr4 = static_cast<uint32_t>(lease->addr_);
  1632. where.buffer_type = MYSQL_TYPE_LONG;
  1633. where.buffer = reinterpret_cast<char*>(&addr4);
  1634. where.is_unsigned = MLM_TRUE;
  1635. bind.push_back(where);
  1636. // Drop to common update code
  1637. updateLeaseCommon(stindex, &bind[0], lease);
  1638. }
  1639. void
  1640. MySqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
  1641. const StatementIndex stindex = UPDATE_LEASE6;
  1642. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1643. DHCPSRV_MYSQL_UPDATE_ADDR6).arg(lease->addr_.toText())
  1644. .arg(lease->type_);
  1645. // Create the MYSQL_BIND array for the data being updated
  1646. std::vector<MYSQL_BIND> bind = exchange6_->createBindForSend(lease);
  1647. // Set up the WHERE clause value
  1648. MYSQL_BIND where;
  1649. memset(&where, 0, sizeof(where));
  1650. std::string addr6 = lease->addr_.toText();
  1651. unsigned long addr6_length = addr6.size();
  1652. // See the earlier description of the use of "const_cast" when accessing
  1653. // the address for an explanation of the reason.
  1654. where.buffer_type = MYSQL_TYPE_STRING;
  1655. where.buffer = const_cast<char*>(addr6.c_str());
  1656. where.buffer_length = addr6_length;
  1657. where.length = &addr6_length;
  1658. bind.push_back(where);
  1659. // Drop to common update code
  1660. updateLeaseCommon(stindex, &bind[0], lease);
  1661. }
  1662. // Delete lease methods. Similar to other groups of methods, these comprise
  1663. // a per-type method that sets up the relevant MYSQL_BIND array (in this
  1664. // case, a single method for both V4 and V6 addresses) and a common method that
  1665. // handles the common processing.
  1666. uint64_t
  1667. MySqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex, MYSQL_BIND* bind) {
  1668. // Bind the input parameters to the statement
  1669. int status = mysql_stmt_bind_param(conn_.statements_[stindex], bind);
  1670. checkError(status, stindex, "unable to bind WHERE clause parameter");
  1671. // Execute
  1672. status = mysql_stmt_execute(conn_.statements_[stindex]);
  1673. checkError(status, stindex, "unable to execute");
  1674. // See how many rows were affected. Note that the statement may delete
  1675. // multiple rows.
  1676. return (static_cast<uint64_t>(mysql_stmt_affected_rows(conn_.statements_[stindex])));
  1677. }
  1678. bool
  1679. MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
  1680. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1681. DHCPSRV_MYSQL_DELETE_ADDR).arg(addr.toText());
  1682. // Set up the WHERE clause value
  1683. MYSQL_BIND inbind[1];
  1684. memset(inbind, 0, sizeof(inbind));
  1685. if (addr.isV4()) {
  1686. uint32_t addr4 = static_cast<uint32_t>(addr);
  1687. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1688. inbind[0].buffer = reinterpret_cast<char*>(&addr4);
  1689. inbind[0].is_unsigned = MLM_TRUE;
  1690. return (deleteLeaseCommon(DELETE_LEASE4, inbind) > 0);
  1691. } else {
  1692. std::string addr6 = addr.toText();
  1693. unsigned long addr6_length = addr6.size();
  1694. // See the earlier description of the use of "const_cast" when accessing
  1695. // the address for an explanation of the reason.
  1696. inbind[0].buffer_type = MYSQL_TYPE_STRING;
  1697. inbind[0].buffer = const_cast<char*>(addr6.c_str());
  1698. inbind[0].buffer_length = addr6_length;
  1699. inbind[0].length = &addr6_length;
  1700. return (deleteLeaseCommon(DELETE_LEASE6, inbind) > 0);
  1701. }
  1702. }
  1703. uint64_t
  1704. MySqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
  1705. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1706. DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED4)
  1707. .arg(secs);
  1708. return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
  1709. }
  1710. uint64_t
  1711. MySqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
  1712. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1713. DHCPSRV_MYSQL_DELETE_EXPIRED_RECLAIMED6)
  1714. .arg(secs);
  1715. return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
  1716. }
  1717. uint64_t
  1718. MySqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
  1719. StatementIndex statement_index) {
  1720. // Set up the WHERE clause value
  1721. MYSQL_BIND inbind[2];
  1722. memset(inbind, 0, sizeof(inbind));
  1723. // State is reclaimed.
  1724. uint32_t state = static_cast<uint32_t>(Lease::STATE_EXPIRED_RECLAIMED);
  1725. inbind[0].buffer_type = MYSQL_TYPE_LONG;
  1726. inbind[0].buffer = reinterpret_cast<char*>(&state);
  1727. inbind[0].is_unsigned = MLM_TRUE;
  1728. // Expiration timestamp.
  1729. MYSQL_TIME expire_time;
  1730. convertToDatabaseTime(time(NULL) - static_cast<time_t>(secs), expire_time);
  1731. inbind[1].buffer_type = MYSQL_TYPE_TIMESTAMP;
  1732. inbind[1].buffer = reinterpret_cast<char*>(&expire_time);
  1733. inbind[1].buffer_length = sizeof(expire_time);
  1734. // Get the number of deleted leases and log it.
  1735. uint64_t deleted_leases = deleteLeaseCommon(statement_index, inbind);
  1736. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1737. DHCPSRV_MYSQL_DELETED_EXPIRED_RECLAIMED)
  1738. .arg(deleted_leases);
  1739. return (deleted_leases);
  1740. }
  1741. // Miscellaneous database methods.
  1742. std::string
  1743. MySqlLeaseMgr::getName() const {
  1744. std::string name = "";
  1745. try {
  1746. name = conn_.getParameter("name");
  1747. } catch (...) {
  1748. // Return an empty name
  1749. }
  1750. return (name);
  1751. }
  1752. std::string
  1753. MySqlLeaseMgr::getDescription() const {
  1754. return (std::string("MySQL Database"));
  1755. }
  1756. std::pair<uint32_t, uint32_t>
  1757. MySqlLeaseMgr::getVersion() const {
  1758. const StatementIndex stindex = GET_VERSION;
  1759. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1760. DHCPSRV_MYSQL_GET_VERSION);
  1761. uint32_t major; // Major version number
  1762. uint32_t minor; // Minor version number
  1763. // Execute the prepared statement
  1764. int status = mysql_stmt_execute(conn_.statements_[stindex]);
  1765. if (status != 0) {
  1766. isc_throw(DbOperationError, "unable to execute <"
  1767. << conn_.text_statements_[stindex] << "> - reason: " <<
  1768. mysql_error(conn_.mysql_));
  1769. }
  1770. // Bind the output of the statement to the appropriate variables.
  1771. MYSQL_BIND bind[2];
  1772. memset(bind, 0, sizeof(bind));
  1773. bind[0].buffer_type = MYSQL_TYPE_LONG;
  1774. bind[0].is_unsigned = 1;
  1775. bind[0].buffer = &major;
  1776. bind[0].buffer_length = sizeof(major);
  1777. bind[1].buffer_type = MYSQL_TYPE_LONG;
  1778. bind[1].is_unsigned = 1;
  1779. bind[1].buffer = &minor;
  1780. bind[1].buffer_length = sizeof(minor);
  1781. status = mysql_stmt_bind_result(conn_.statements_[stindex], bind);
  1782. if (status != 0) {
  1783. isc_throw(DbOperationError, "unable to bind result set: " <<
  1784. mysql_error(conn_.mysql_));
  1785. }
  1786. // Fetch the data and set up the "release" object to release associated
  1787. // resources when this method exits then retrieve the data.
  1788. MySqlFreeResult fetch_release(conn_.statements_[stindex]);
  1789. status = mysql_stmt_fetch(conn_.statements_[stindex]);
  1790. if (status != 0) {
  1791. isc_throw(DbOperationError, "unable to obtain result set: " <<
  1792. mysql_error(conn_.mysql_));
  1793. }
  1794. return (std::make_pair(major, minor));
  1795. }
  1796. void
  1797. MySqlLeaseMgr::commit() {
  1798. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_COMMIT);
  1799. if (mysql_commit(conn_.mysql_) != 0) {
  1800. isc_throw(DbOperationError, "commit failed: " << mysql_error(conn_.mysql_));
  1801. }
  1802. }
  1803. void
  1804. MySqlLeaseMgr::rollback() {
  1805. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MYSQL_ROLLBACK);
  1806. if (mysql_rollback(conn_.mysql_) != 0) {
  1807. isc_throw(DbOperationError, "rollback failed: " << mysql_error(conn_.mysql_));
  1808. }
  1809. }
  1810. void
  1811. MySqlLeaseMgr::checkError(int status, StatementIndex index,
  1812. const char* what) const {
  1813. if (status != 0) {
  1814. switch(mysql_errno(conn_.mysql_)) {
  1815. // These are the ones we consider fatal. Remember this method is
  1816. // used to check errors of API calls made subsequent to successfully
  1817. // connecting. Errors occuring while attempting to connect are
  1818. // checked in the connection code. An alternative would be to call
  1819. // mysql_ping() - assuming autoreconnect is off. If that fails
  1820. // then we know connection is toast.
  1821. case CR_SERVER_GONE_ERROR:
  1822. case CR_SERVER_LOST:
  1823. case CR_OUT_OF_MEMORY:
  1824. case CR_CONNECTION_ERROR:
  1825. // We're exiting on fatal
  1826. LOG_ERROR(dhcpsrv_logger, DHCPSRV_MYSQL_FATAL_ERROR)
  1827. .arg(what)
  1828. .arg(conn_.text_statements_[index])
  1829. .arg(mysql_error(conn_.mysql_))
  1830. .arg(mysql_errno(conn_.mysql_));
  1831. exit (-1);
  1832. default:
  1833. // Connection is ok, so it must be an SQL error
  1834. isc_throw(DbOperationError, what << " for <"
  1835. << conn_.text_statements_[index] << ">, reason: "
  1836. << mysql_error(conn_.mysql_) << " (error code "
  1837. << mysql_errno(conn_.mysql_) << ")");
  1838. }
  1839. }
  1840. }
  1841. }; // end of isc::dhcp namespace
  1842. }; // end of isc namespace