pgsql_lease_mgr.cc 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. // Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <asiolink/io_address.h>
  8. #include <dhcp/duid.h>
  9. #include <dhcp/hwaddr.h>
  10. #include <dhcpsrv/dhcpsrv_log.h>
  11. #include <dhcpsrv/pgsql_lease_mgr.h>
  12. #include <boost/static_assert.hpp>
  13. #include <iomanip>
  14. #include <limits>
  15. #include <sstream>
  16. #include <string>
  17. #include <time.h>
  18. using namespace isc;
  19. using namespace isc::dhcp;
  20. using namespace std;
  21. namespace {
  22. /// @todo TKM lease6 needs to accomodate hwaddr,hwtype, and hwaddr source
  23. /// columns. This is coverd by tickets #3557, #4530, and PR#9.
  24. /// @brief Catalog of all the SQL statements currently supported. Note
  25. /// that the order columns appear in statement body must match the order they
  26. /// that the occur in the table. This does not apply to the where clause.
  27. PgSqlTaggedStatement tagged_statements[] = {
  28. // DELETE_LEASE4
  29. { 1, { OID_INT8 },
  30. "delete_lease4",
  31. "DELETE FROM lease4 WHERE address = $1"},
  32. // DELETE_LEASE4_STATE_EXPIRED
  33. { 2, { OID_INT8, OID_TIMESTAMP },
  34. "delete_lease4_state_expired",
  35. "DELETE FROM lease4 "
  36. "WHERE state = $1 AND expire < $2"},
  37. // DELETE_LEASE6
  38. { 1, { OID_VARCHAR },
  39. "delete_lease6",
  40. "DELETE FROM lease6 WHERE address = $1"},
  41. // DELETE_LEASE6_STATE_EXPIRED
  42. { 2, { OID_INT8, OID_TIMESTAMP },
  43. "delete_lease6_state_expired",
  44. "DELETE FROM lease6 "
  45. "WHERE state = $1 AND expire < $2"},
  46. // GET_LEASE4_ADDR
  47. { 1, { OID_INT8 },
  48. "get_lease4_addr",
  49. "SELECT address, hwaddr, client_id, "
  50. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  51. "fqdn_fwd, fqdn_rev, hostname, state "
  52. "FROM lease4 "
  53. "WHERE address = $1"},
  54. // GET_LEASE4_CLIENTID
  55. { 1, { OID_BYTEA },
  56. "get_lease4_clientid",
  57. "SELECT address, hwaddr, client_id, "
  58. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  59. "fqdn_fwd, fqdn_rev, hostname, state "
  60. "FROM lease4 "
  61. "WHERE client_id = $1"},
  62. // GET_LEASE4_CLIENTID_SUBID
  63. { 2, { OID_BYTEA, OID_INT8 },
  64. "get_lease4_clientid_subid",
  65. "SELECT address, hwaddr, client_id, "
  66. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  67. "fqdn_fwd, fqdn_rev, hostname, state "
  68. "FROM lease4 "
  69. "WHERE client_id = $1 AND subnet_id = $2"},
  70. // GET_LEASE4_HWADDR
  71. { 1, { OID_BYTEA },
  72. "get_lease4_hwaddr",
  73. "SELECT address, hwaddr, client_id, "
  74. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  75. "fqdn_fwd, fqdn_rev, hostname, state "
  76. "FROM lease4 "
  77. "WHERE hwaddr = $1"},
  78. // GET_LEASE4_HWADDR_SUBID
  79. { 2, { OID_BYTEA, OID_INT8 },
  80. "get_lease4_hwaddr_subid",
  81. "SELECT address, hwaddr, client_id, "
  82. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  83. "fqdn_fwd, fqdn_rev, hostname, state "
  84. "FROM lease4 "
  85. "WHERE hwaddr = $1 AND subnet_id = $2"},
  86. // GET_LEASE4_EXPIRE
  87. { 3, { OID_INT8, OID_TIMESTAMP, OID_INT8 },
  88. "get_lease4_expire",
  89. "SELECT address, hwaddr, client_id, "
  90. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  91. "fqdn_fwd, fqdn_rev, hostname, state "
  92. "FROM lease4 "
  93. "WHERE state != $1 AND expire < $2 "
  94. "ORDER BY expire "
  95. "LIMIT $3"},
  96. // GET_LEASE6_ADDR
  97. { 2, { OID_VARCHAR, OID_INT2 },
  98. "get_lease6_addr",
  99. "SELECT address, duid, valid_lifetime, "
  100. "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
  101. "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
  102. "state "
  103. "FROM lease6 "
  104. "WHERE address = $1 AND lease_type = $2"},
  105. // GET_LEASE6_DUID_IAID
  106. { 3, { OID_BYTEA, OID_INT8, OID_INT2 },
  107. "get_lease6_duid_iaid",
  108. "SELECT address, duid, valid_lifetime, "
  109. "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
  110. "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
  111. "state "
  112. "FROM lease6 "
  113. "WHERE duid = $1 AND iaid = $2 AND lease_type = $3"},
  114. // GET_LEASE6_DUID_IAID_SUBID
  115. { 4, { OID_INT2, OID_BYTEA, OID_INT8, OID_INT8 },
  116. "get_lease6_duid_iaid_subid",
  117. "SELECT address, duid, valid_lifetime, "
  118. "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
  119. "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, "
  120. "state "
  121. "FROM lease6 "
  122. "WHERE lease_type = $1 "
  123. "AND duid = $2 AND iaid = $3 AND subnet_id = $4"},
  124. // GET_LEASE6_EXPIRE
  125. { 3, { OID_INT8, OID_TIMESTAMP, OID_INT8 },
  126. "get_lease6_expire",
  127. "SELECT address, duid, valid_lifetime, "
  128. "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
  129. "lease_type, iaid, prefix_len, "
  130. "fqdn_fwd, fqdn_rev, hostname, state "
  131. "state "
  132. "FROM lease6 "
  133. "WHERE state != $1 AND expire < $2 "
  134. "ORDER BY expire "
  135. "LIMIT $3"},
  136. // GET_VERSION
  137. { 0, { OID_NONE },
  138. "get_version",
  139. "SELECT version, minor FROM schema_version"},
  140. // INSERT_LEASE4
  141. { 10, { OID_INT8, OID_BYTEA, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8,
  142. OID_BOOL, OID_BOOL, OID_VARCHAR, OID_INT8, OID_INT8 },
  143. "insert_lease4",
  144. "INSERT INTO lease4(address, hwaddr, client_id, "
  145. "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, "
  146. "state) "
  147. "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)"},
  148. // INSERT_LEASE6
  149. { 13, { OID_VARCHAR, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8,
  150. OID_INT8, OID_INT2, OID_INT8, OID_INT2, OID_BOOL, OID_BOOL,
  151. OID_VARCHAR, OID_INT8 },
  152. "insert_lease6",
  153. "INSERT INTO lease6(address, duid, valid_lifetime, "
  154. "expire, subnet_id, pref_lifetime, "
  155. "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, state) "
  156. "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)"},
  157. // UPDATE_LEASE4
  158. { 11, { OID_INT8, OID_BYTEA, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8,
  159. OID_BOOL, OID_BOOL, OID_VARCHAR, OID_INT8, OID_INT8 },
  160. "update_lease4",
  161. "UPDATE lease4 SET address = $1, hwaddr = $2, "
  162. "client_id = $3, valid_lifetime = $4, expire = $5, "
  163. "subnet_id = $6, fqdn_fwd = $7, fqdn_rev = $8, hostname = $9, "
  164. "state = $10"
  165. "WHERE address = $11"},
  166. // UPDATE_LEASE6
  167. { 14, { OID_VARCHAR, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8, OID_INT8,
  168. OID_INT2, OID_INT8, OID_INT2, OID_BOOL, OID_BOOL, OID_VARCHAR,
  169. OID_INT8, OID_VARCHAR },
  170. "update_lease6",
  171. "UPDATE lease6 SET address = $1, duid = $2, "
  172. "valid_lifetime = $3, expire = $4, subnet_id = $5, "
  173. "pref_lifetime = $6, lease_type = $7, iaid = $8, "
  174. "prefix_len = $9, fqdn_fwd = $10, fqdn_rev = $11, hostname = $12, "
  175. "state = $13 "
  176. "WHERE address = $14"},
  177. // End of list sentinel
  178. { 0, { 0 }, NULL, NULL}
  179. };
  180. };
  181. namespace isc {
  182. namespace dhcp {
  183. /// @brief Base class for marshalling leases to and from PostgreSQL.
  184. ///
  185. /// Provides the common functionality to set up binding information between
  186. /// lease objects in the program and their database representation in the
  187. /// database.
  188. class PgSqlLeaseExchange : public PgSqlExchange {
  189. public:
  190. PgSqlLeaseExchange()
  191. : addr_str_(""), valid_lifetime_(0), valid_lft_str_(""),
  192. expire_(0), expire_str_(""), subnet_id_(0), subnet_id_str_(""),
  193. cltt_(0), fqdn_fwd_(false), fqdn_rev_(false), hostname_(""),
  194. state_str_("") {
  195. }
  196. virtual ~PgSqlLeaseExchange(){}
  197. protected:
  198. /// @brief Common Instance members used for binding and conversion
  199. //@{
  200. std::string addr_str_;
  201. uint32_t valid_lifetime_;
  202. std::string valid_lft_str_;
  203. time_t expire_;
  204. std::string expire_str_;
  205. uint32_t subnet_id_;
  206. std::string subnet_id_str_;
  207. time_t cltt_;
  208. bool fqdn_fwd_;
  209. bool fqdn_rev_;
  210. std::string hostname_;
  211. std::string state_str_;
  212. //@}
  213. };
  214. /// @brief Supports exchanging IPv4 leases with PostgreSQL.
  215. class PgSqlLease4Exchange : public PgSqlLeaseExchange {
  216. private:
  217. /// @brief Column numbers for each column in the Lease4 table.
  218. /// These are used for both retrieving data and for looking up
  219. /// column labels for logging. Note that their numeric order
  220. /// MUST match that of the column order in the Lease4 table.
  221. static const size_t ADDRESS_COL = 0;
  222. static const size_t HWADDR_COL = 1;
  223. static const size_t CLIENT_ID_COL = 2;
  224. static const size_t VALID_LIFETIME_COL = 3;
  225. static const size_t EXPIRE_COL = 4;
  226. static const size_t SUBNET_ID_COL = 5;
  227. static const size_t FQDN_FWD_COL = 6;
  228. static const size_t FQDN_REV_COL = 7;
  229. static const size_t HOSTNAME_COL = 8;
  230. static const size_t STATE_COL = 9;
  231. /// @brief Number of columns in the table holding DHCPv4 leases.
  232. static const size_t LEASE_COLUMNS = 10;
  233. public:
  234. /// @brief Default constructor
  235. PgSqlLease4Exchange()
  236. : lease_(), addr4_(0), hwaddr_length_(0), hwaddr_(hwaddr_length_),
  237. client_id_length_(0) {
  238. BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS);
  239. memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
  240. memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
  241. // Set the column names (for error messages)
  242. columns_.push_back("address");
  243. columns_.push_back("hwaddr");
  244. columns_.push_back("client_id");
  245. columns_.push_back("valid_lifetime");
  246. columns_.push_back("expire");
  247. columns_.push_back("subnet_id");
  248. columns_.push_back("fqdn_fwd");
  249. columns_.push_back("fqdn_rev");
  250. columns_.push_back("hostname");
  251. columns_.push_back("state");
  252. }
  253. /// @brief Creates the bind array for sending Lease4 data to the database.
  254. ///
  255. /// Converts each Lease4 member into the appropriate form and adds it
  256. /// to the bind array. Note that the array additions must occur in the
  257. /// order the columns are specified in the SQL statement. By convention
  258. /// all columns in the table are explicitly listed in the SQL statement(s)
  259. /// in the same order as they occur in the table.
  260. ///
  261. /// @param lease Lease4 object that is to be written to the database
  262. /// @param[out] bind_array array to populate with the lease data values
  263. ///
  264. /// @throw DbOperationError if bind_array cannot be populated.
  265. void createBindForSend(const Lease4Ptr& lease, PsqlBindArray& bind_array) {
  266. if (!lease) {
  267. isc_throw(BadValue, "createBindForSend:: Lease4 object is NULL");
  268. }
  269. // Store lease object to ensure it remains valid.
  270. lease_ = lease;
  271. try {
  272. addr_str_ = boost::lexical_cast<std::string>
  273. (static_cast<uint32_t>(lease->addr_));
  274. bind_array.add(addr_str_);
  275. if (lease->hwaddr_ && !lease->hwaddr_->hwaddr_.empty()) {
  276. // PostgreSql does not provide MAX on variable length types
  277. // so we have to enforce it ourselves.
  278. if (lease->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
  279. isc_throw(DbOperationError, "Hardware address length : "
  280. << lease_->hwaddr_->hwaddr_.size()
  281. << " exceeds maximum allowed of: "
  282. << HWAddr::MAX_HWADDR_LEN);
  283. }
  284. bind_array.add(lease->hwaddr_->hwaddr_);
  285. } else {
  286. bind_array.add("");
  287. }
  288. if (lease->client_id_) {
  289. bind_array.add(lease->client_id_->getClientId());
  290. } else {
  291. bind_array.add("");
  292. }
  293. valid_lft_str_ = boost::lexical_cast<std::string>
  294. (lease->valid_lft_);
  295. bind_array.add(valid_lft_str_);
  296. expire_str_ = convertToDatabaseTime(lease->cltt_, lease_->valid_lft_);
  297. bind_array.add(expire_str_);
  298. subnet_id_str_ = boost::lexical_cast<std::string>
  299. (lease->subnet_id_);
  300. bind_array.add(subnet_id_str_);
  301. bind_array.add(lease->fqdn_fwd_);
  302. bind_array.add(lease->fqdn_rev_);
  303. bind_array.add(lease->hostname_);
  304. state_str_ = boost::lexical_cast<std::string>(lease->state_);
  305. bind_array.add(state_str_);
  306. } catch (const std::exception& ex) {
  307. isc_throw(DbOperationError,
  308. "Could not create bind array from Lease4: "
  309. << lease_->addr_.toText() << ", reason: " << ex.what());
  310. }
  311. }
  312. /// @brief Creates a Lease4 object from a given row in a result set.
  313. ///
  314. /// @param r result set containing one or rows from the Lease4 table
  315. /// @param row row number within the result set from to create the Lease4
  316. /// object.
  317. ///
  318. /// @return Lease4Ptr to the newly created Lease4 object
  319. /// @throw DbOperationError if the lease cannot be created.
  320. Lease4Ptr convertFromDatabase(const PgSqlResult& r, int row) {
  321. try {
  322. getColumnValue(r, row, ADDRESS_COL, addr4_);
  323. convertFromBytea(r, row, HWADDR_COL, hwaddr_buffer_,
  324. sizeof(hwaddr_buffer_), hwaddr_length_);
  325. convertFromBytea(r, row, CLIENT_ID_COL, client_id_buffer_,
  326. sizeof(client_id_buffer_), client_id_length_);
  327. getColumnValue(r, row, VALID_LIFETIME_COL, valid_lifetime_);
  328. expire_ = convertFromDatabaseTime(getRawColumnValue(r, row,
  329. EXPIRE_COL));
  330. getColumnValue(r, row , SUBNET_ID_COL, subnet_id_);
  331. cltt_ = expire_ - valid_lifetime_;
  332. getColumnValue(r, row, FQDN_FWD_COL, fqdn_fwd_);
  333. getColumnValue(r, row, FQDN_REV_COL, fqdn_rev_);
  334. hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
  335. HWAddrPtr hwaddr(new HWAddr(hwaddr_buffer_, hwaddr_length_,
  336. HTYPE_ETHER));
  337. return (Lease4Ptr(new Lease4(addr4_, hwaddr,
  338. client_id_buffer_, client_id_length_,
  339. valid_lifetime_, 0, 0, cltt_,
  340. subnet_id_, fqdn_fwd_, fqdn_rev_,
  341. hostname_)));
  342. } catch (const std::exception& ex) {
  343. isc_throw(DbOperationError,
  344. "Could not convert data to Lease4, reason: "
  345. << ex.what());
  346. }
  347. }
  348. private:
  349. /// @brief Lease4 object currently being sent to the database.
  350. /// Storing this value ensures that it remains in scope while any bindings
  351. /// that refer to its contents are in use.
  352. Lease4Ptr lease_;
  353. /// @Brief Lease4 specific members used for binding and conversion.
  354. uint32_t addr4_;
  355. size_t hwaddr_length_;
  356. std::vector<uint8_t> hwaddr_;
  357. uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
  358. size_t client_id_length_;
  359. uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
  360. };
  361. /// @brief Supports exchanging IPv6 leases with PostgreSQL.
  362. class PgSqlLease6Exchange : public PgSqlLeaseExchange {
  363. private:
  364. /// @brief Column numbers for each column in the Lease6 table.
  365. /// These are used for both retrieving data and for looking up
  366. /// column labels for logging. Note that their numeric order
  367. /// MUST match that of the column order in the Lease6 table.
  368. //@{
  369. static const int ADDRESS_COL = 0;
  370. static const int DUID_COL = 1;
  371. static const int VALID_LIFETIME_COL = 2;
  372. static const int EXPIRE_COL = 3;
  373. static const int SUBNET_ID_COL = 4;
  374. static const int PREF_LIFETIME_COL = 5;
  375. static const int LEASE_TYPE_COL = 6;
  376. static const int IAID_COL = 7;
  377. static const int PREFIX_LEN_COL = 8;
  378. static const int FQDN_FWD_COL = 9;
  379. static const int FQDN_REV_COL = 10;
  380. static const int HOSTNAME_COL = 11;
  381. static const int STATE_COL = 12;
  382. //@}
  383. /// @brief Number of columns in the table holding DHCPv6 leases.
  384. static const size_t LEASE_COLUMNS = 13;
  385. public:
  386. PgSqlLease6Exchange()
  387. : lease_(), duid_length_(0), duid_(), iaid_u_(0), iaid_str_(""),
  388. lease_type_(Lease6::TYPE_NA), lease_type_str_(""), prefix_len_(0),
  389. prefix_len_str_(""), pref_lifetime_(0), preferred_lft_str_("") {
  390. BOOST_STATIC_ASSERT(12 < LEASE_COLUMNS);
  391. memset(duid_buffer_, 0, sizeof(duid_buffer_));
  392. // Set the column names (for error messages)
  393. columns_.push_back("address");
  394. columns_.push_back("duid");
  395. columns_.push_back("valid_lifetime");
  396. columns_.push_back("expire");
  397. columns_.push_back("subnet_id");
  398. columns_.push_back("pref_lifetime");
  399. columns_.push_back("lease_type");
  400. columns_.push_back("iaid");
  401. columns_.push_back("prefix_len");
  402. columns_.push_back("fqdn_fwd");
  403. columns_.push_back("fqdn_rev");
  404. columns_.push_back("hostname");
  405. columns_.push_back("state");
  406. }
  407. /// @brief Creates the bind array for sending Lease6 data to the database.
  408. ///
  409. /// Converts each Lease6 member into the appropriate form and adds it
  410. /// to the bind array. Note that the array additions must occur in the
  411. /// order the columns are specified in the SQL statement. By convention
  412. /// all columns in the table are explicitly listed in the SQL statement(s)
  413. /// in the same order as they occur in the table.
  414. ///
  415. /// @param lease Lease6 object that is to be written to the database
  416. /// @param[out] bind_array array to populate with the lease data values
  417. ///
  418. /// @throw DbOperationError if bind_array cannot be populated.
  419. void createBindForSend(const Lease6Ptr& lease, PsqlBindArray& bind_array) {
  420. if (!lease) {
  421. isc_throw(BadValue, "createBindForSend:: Lease6 object is NULL");
  422. }
  423. // Store lease object to ensure it remains valid.
  424. lease_ = lease;
  425. try {
  426. addr_str_ = lease_->addr_.toText();
  427. bind_array.add(addr_str_);
  428. if (lease_->duid_) {
  429. bind_array.add(lease_->duid_->getDuid());
  430. } else {
  431. isc_throw (BadValue, "IPv6 Lease cannot have a null DUID");
  432. }
  433. valid_lft_str_ = boost::lexical_cast<std::string>
  434. (lease->valid_lft_);
  435. bind_array.add(valid_lft_str_);
  436. expire_str_ = convertToDatabaseTime(lease->cltt_, lease_->valid_lft_);
  437. bind_array.add(expire_str_);
  438. subnet_id_str_ = boost::lexical_cast<std::string>
  439. (lease->subnet_id_);
  440. bind_array.add(subnet_id_str_);
  441. preferred_lft_str_ = boost::lexical_cast<std::string>
  442. (lease_->preferred_lft_);
  443. bind_array.add(preferred_lft_str_);
  444. lease_type_str_ = boost::lexical_cast<std::string>(lease_->type_);
  445. bind_array.add(lease_type_str_);
  446. // The iaid is stored as an INT in lease6 table, so we must
  447. // lexically cast from an integer version to avoid out of range
  448. // exception failure upon insert.
  449. iaid_u_.uval_ = lease_->iaid_;
  450. iaid_str_ = boost::lexical_cast<std::string>(iaid_u_.ival_);
  451. bind_array.add(iaid_str_);
  452. prefix_len_str_ = boost::lexical_cast<std::string>
  453. (static_cast<unsigned int>(lease_->prefixlen_));
  454. bind_array.add(prefix_len_str_);
  455. bind_array.add(lease->fqdn_fwd_);
  456. bind_array.add(lease->fqdn_rev_);
  457. bind_array.add(lease->hostname_);
  458. state_str_ = boost::lexical_cast<std::string>(lease->state_);
  459. bind_array.add(state_str_);
  460. } catch (const std::exception& ex) {
  461. isc_throw(DbOperationError,
  462. "Could not create bind array from Lease6: "
  463. << lease_->addr_.toText() << ", reason: " << ex.what());
  464. }
  465. }
  466. /// @brief Creates a Lease6 object from a given row in a result set.
  467. ///
  468. /// @param r result set containing one or rows from the Lease6 table
  469. /// @param row row number within the result set from to create the Lease6
  470. /// object.
  471. ///
  472. /// @return Lease6Ptr to the newly created Lease4 object
  473. /// @throw DbOperationError if the lease cannot be created.
  474. Lease6Ptr convertFromDatabase(const PgSqlResult& r, int row) {
  475. try {
  476. isc::asiolink::IOAddress addr(getIPv6Value(r, row, ADDRESS_COL));
  477. convertFromBytea(r, row, DUID_COL, duid_buffer_,
  478. sizeof(duid_buffer_), duid_length_);
  479. DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
  480. getColumnValue(r, row, VALID_LIFETIME_COL, valid_lifetime_);
  481. expire_ = convertFromDatabaseTime(getRawColumnValue(r, row,
  482. EXPIRE_COL));
  483. cltt_ = expire_ - valid_lifetime_;
  484. getColumnValue(r, row , SUBNET_ID_COL, subnet_id_);
  485. getColumnValue(r, row , PREF_LIFETIME_COL, pref_lifetime_);
  486. getLeaseTypeColumnValue(r, row, LEASE_TYPE_COL, lease_type_);
  487. getColumnValue(r, row , IAID_COL, iaid_u_.ival_);
  488. getColumnValue(r, row , PREFIX_LEN_COL, prefix_len_);
  489. getColumnValue(r, row, FQDN_FWD_COL, fqdn_fwd_);
  490. getColumnValue(r, row, FQDN_REV_COL, fqdn_rev_);
  491. hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
  492. /// @todo: implement this in #3557.
  493. HWAddrPtr hwaddr;
  494. Lease6Ptr result(new Lease6(lease_type_, addr, duid_ptr,
  495. iaid_u_.uval_, pref_lifetime_,
  496. valid_lifetime_, 0, 0,
  497. subnet_id_, fqdn_fwd_, fqdn_rev_,
  498. hostname_, hwaddr, prefix_len_));
  499. result->cltt_ = cltt_;
  500. return (result);
  501. } catch (const std::exception& ex) {
  502. isc_throw(DbOperationError,
  503. "Could not convert data to Lease6, reason: "
  504. << ex.what());
  505. }
  506. }
  507. /// @brief Fetches an integer text column as a Lease6::Type
  508. ///
  509. /// @param r the result set containing the query results
  510. /// @param row the row number within the result set
  511. /// @param col the column number within the row
  512. /// @param[out] value parameter to receive the converted value
  513. ///
  514. /// Note we depart from overloading getColumnValue to avoid ambiguity
  515. /// with base class methods for integers.
  516. ///
  517. /// @throw DbOperationError if the value cannot be fetched or is
  518. /// invalid.
  519. void getLeaseTypeColumnValue(const PgSqlResult& r, const int row,
  520. const size_t col, Lease6::Type& value) const {
  521. uint32_t raw_value = 0;
  522. getColumnValue(r, row , col, raw_value);
  523. switch (raw_value) {
  524. case Lease6::TYPE_NA:
  525. case Lease6::TYPE_TA:
  526. case Lease6::TYPE_PD:
  527. value = static_cast<Lease6::Type>(raw_value);
  528. break;
  529. default:
  530. isc_throw(DbOperationError, "Invalid lease type: " << raw_value
  531. << " for: " << getColumnLabel(r, col) << " row:" << row);
  532. }
  533. }
  534. private:
  535. /// @brief Lease6 object currently being sent to the database.
  536. /// Storing this value ensures that it remains in scope while any bindings
  537. /// that refer to its contents are in use.
  538. Lease6Ptr lease_;
  539. /// @brief Lease6 specific members for binding and conversion.
  540. //@{
  541. size_t duid_length_;
  542. vector<uint8_t> duid_;
  543. uint8_t duid_buffer_[DUID::MAX_DUID_LEN];
  544. /// @brief Union for marshalling IAID into and out of the database
  545. /// IAID is defined in the RFC as 4 octets, which Kea code handles as
  546. /// a uint32_t. Postgresql however, offers only signed integer types
  547. /// of sizes 2, 4, and 8 bytes (SMALLINT, INT, and BIGINT respectively).
  548. /// IAID is used in several indexes so rather than use the BIGINT, we
  549. /// use this union to safely move the value into and out of an INT column.
  550. union Uiaid {
  551. Uiaid(uint32_t val) : uval_(val){};
  552. Uiaid(int32_t val) : ival_(val){};
  553. uint32_t uval_;
  554. int32_t ival_;
  555. } iaid_u_;
  556. std::string iaid_str_;
  557. Lease6::Type lease_type_;
  558. std::string lease_type_str_;
  559. uint8_t prefix_len_;
  560. std::string prefix_len_str_;
  561. uint32_t pref_lifetime_;
  562. std::string preferred_lft_str_;
  563. //@}
  564. };
  565. PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
  566. : LeaseMgr(), exchange4_(new PgSqlLease4Exchange()),
  567. exchange6_(new PgSqlLease6Exchange()), conn_(parameters) {
  568. conn_.openDatabase();
  569. int i = 0;
  570. for( ; tagged_statements[i].text != NULL ; ++i) {
  571. conn_.prepareStatement(tagged_statements[i]);
  572. }
  573. // Just in case somebody foo-barred things
  574. if (i != NUM_STATEMENTS) {
  575. isc_throw(DbOpenError, "Number of statements prepared: " << i
  576. << " does not match expected count:" << NUM_STATEMENTS);
  577. }
  578. pair<uint32_t, uint32_t> code_version(PG_CURRENT_VERSION, PG_CURRENT_MINOR);
  579. pair<uint32_t, uint32_t> db_version = getVersion();
  580. if (code_version != db_version) {
  581. isc_throw(DbOpenError, "Posgresql schema version mismatch: need version: "
  582. << code_version.first << "." << code_version.second
  583. << " found version: " << db_version.first << "." << db_version.second);
  584. }
  585. }
  586. PgSqlLeaseMgr::~PgSqlLeaseMgr() {
  587. }
  588. std::string
  589. PgSqlLeaseMgr::getDBVersion() {
  590. std::stringstream tmp;
  591. tmp << "PostgreSQL backend " << PG_CURRENT_VERSION;
  592. tmp << "." << PG_CURRENT_MINOR;
  593. tmp << ", library " << PQlibVersion();
  594. return (tmp.str());
  595. }
  596. bool
  597. PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
  598. PsqlBindArray& bind_array) {
  599. PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
  600. tagged_statements[stindex].nbparams,
  601. &bind_array.values_[0],
  602. &bind_array.lengths_[0],
  603. &bind_array.formats_[0], 0));
  604. int s = PQresultStatus(r);
  605. if (s != PGRES_COMMAND_OK) {
  606. // Failure: check for the special case of duplicate entry. If this is
  607. // the case, we return false to indicate that the row was not added.
  608. // Otherwise we throw an exception.
  609. if (conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
  610. return (false);
  611. }
  612. conn_.checkStatementError(r, tagged_statements[stindex]);
  613. }
  614. return (true);
  615. }
  616. bool
  617. PgSqlLeaseMgr::addLease(const Lease4Ptr& lease) {
  618. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  619. DHCPSRV_PGSQL_ADD_ADDR4).arg(lease->addr_.toText());
  620. PsqlBindArray bind_array;
  621. exchange4_->createBindForSend(lease, bind_array);
  622. return (addLeaseCommon(INSERT_LEASE4, bind_array));
  623. }
  624. bool
  625. PgSqlLeaseMgr::addLease(const Lease6Ptr& lease) {
  626. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  627. DHCPSRV_PGSQL_ADD_ADDR6).arg(lease->addr_.toText());
  628. PsqlBindArray bind_array;
  629. exchange6_->createBindForSend(lease, bind_array);
  630. return (addLeaseCommon(INSERT_LEASE6, bind_array));
  631. }
  632. template <typename Exchange, typename LeaseCollection>
  633. void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
  634. PsqlBindArray& bind_array,
  635. Exchange& exchange,
  636. LeaseCollection& result,
  637. bool single) const {
  638. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  639. DHCPSRV_PGSQL_GET_ADDR4).arg(tagged_statements[stindex].name);
  640. PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
  641. tagged_statements[stindex].nbparams,
  642. &bind_array.values_[0],
  643. &bind_array.lengths_[0],
  644. &bind_array.formats_[0], 0));
  645. conn_.checkStatementError(r, tagged_statements[stindex]);
  646. int rows = PQntuples(r);
  647. if (single && rows > 1) {
  648. isc_throw(MultipleRecords, "multiple records were found in the "
  649. "database where only one was expected for query "
  650. << tagged_statements[stindex].name);
  651. }
  652. for(int i = 0; i < rows; ++ i) {
  653. result.push_back(exchange->convertFromDatabase(r, i));
  654. }
  655. }
  656. void
  657. PgSqlLeaseMgr::getLease(StatementIndex stindex, PsqlBindArray& bind_array,
  658. Lease4Ptr& result) const {
  659. // Create appropriate collection object and get all leases matching
  660. // the selection criteria. The "single" parameter is true to indicate
  661. // that the called method should throw an exception if multiple
  662. // matching records are found: this particular method is called when only
  663. // one or zero matches is expected.
  664. Lease4Collection collection;
  665. getLeaseCollection(stindex, bind_array, exchange4_, collection, true);
  666. // Return single record if present, else clear the lease.
  667. if (collection.empty()) {
  668. result.reset();
  669. } else {
  670. result = *collection.begin();
  671. }
  672. }
  673. void
  674. PgSqlLeaseMgr::getLease(StatementIndex stindex, PsqlBindArray& bind_array,
  675. Lease6Ptr& result) const {
  676. // Create appropriate collection object and get all leases matching
  677. // the selection criteria. The "single" parameter is true to indicate
  678. // that the called method should throw an exception if multiple
  679. // matching records are found: this particular method is called when only
  680. // one or zero matches is expected.
  681. Lease6Collection collection;
  682. getLeaseCollection(stindex, bind_array, exchange6_, collection, true);
  683. // Return single record if present, else clear the lease.
  684. if (collection.empty()) {
  685. result.reset();
  686. } else {
  687. result = *collection.begin();
  688. }
  689. }
  690. Lease4Ptr
  691. PgSqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
  692. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  693. DHCPSRV_PGSQL_GET_ADDR4).arg(addr.toText());
  694. // Set up the WHERE clause value
  695. PsqlBindArray bind_array;
  696. // LEASE ADDRESS
  697. std::string addr_str = boost::lexical_cast<std::string>
  698. (static_cast<uint32_t>(addr));
  699. bind_array.add(addr_str);
  700. // Get the data
  701. Lease4Ptr result;
  702. getLease(GET_LEASE4_ADDR, bind_array, result);
  703. return (result);
  704. }
  705. Lease4Collection
  706. PgSqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
  707. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  708. DHCPSRV_PGSQL_GET_HWADDR).arg(hwaddr.toText());
  709. // Set up the WHERE clause value
  710. PsqlBindArray bind_array;
  711. // HWADDR
  712. if (!hwaddr.hwaddr_.empty()) {
  713. bind_array.add(hwaddr.hwaddr_);
  714. } else {
  715. bind_array.add("");
  716. }
  717. // Get the data
  718. Lease4Collection result;
  719. getLeaseCollection(GET_LEASE4_HWADDR, bind_array, result);
  720. return (result);
  721. }
  722. Lease4Ptr
  723. PgSqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
  724. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  725. DHCPSRV_PGSQL_GET_SUBID_HWADDR)
  726. .arg(subnet_id).arg(hwaddr.toText());
  727. // Set up the WHERE clause value
  728. PsqlBindArray bind_array;
  729. // HWADDR
  730. if (!hwaddr.hwaddr_.empty()) {
  731. bind_array.add(hwaddr.hwaddr_);
  732. } else {
  733. bind_array.add("");
  734. }
  735. // SUBNET_ID
  736. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  737. bind_array.add(subnet_id_str);
  738. // Get the data
  739. Lease4Ptr result;
  740. getLease(GET_LEASE4_HWADDR_SUBID, bind_array, result);
  741. return (result);
  742. }
  743. Lease4Collection
  744. PgSqlLeaseMgr::getLease4(const ClientId& clientid) const {
  745. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  746. DHCPSRV_PGSQL_GET_CLIENTID).arg(clientid.toText());
  747. // Set up the WHERE clause value
  748. PsqlBindArray bind_array;
  749. // CLIENT_ID
  750. bind_array.add(clientid.getClientId());
  751. // Get the data
  752. Lease4Collection result;
  753. getLeaseCollection(GET_LEASE4_CLIENTID, bind_array, result);
  754. return (result);
  755. }
  756. Lease4Ptr
  757. PgSqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
  758. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  759. DHCPSRV_PGSQL_GET_SUBID_CLIENTID)
  760. .arg(subnet_id).arg(clientid.toText());
  761. // Set up the WHERE clause value
  762. PsqlBindArray bind_array;
  763. // CLIENT_ID
  764. bind_array.add(clientid.getClientId());
  765. // SUBNET_ID
  766. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  767. bind_array.add(subnet_id_str);
  768. // Get the data
  769. Lease4Ptr result;
  770. getLease(GET_LEASE4_CLIENTID_SUBID, bind_array, result);
  771. return (result);
  772. }
  773. Lease4Ptr
  774. PgSqlLeaseMgr::getLease4(const ClientId&, const HWAddr&, SubnetID) const {
  775. /// This function is currently not implemented because allocation engine
  776. /// searches for the lease using HW address or client identifier.
  777. /// It never uses both parameters in the same time. We need to
  778. /// consider if this function is needed at all.
  779. isc_throw(NotImplemented, "The PgSqlLeaseMgr::getLease4 function was"
  780. " called, but it is not implemented");
  781. }
  782. Lease6Ptr
  783. PgSqlLeaseMgr::getLease6(Lease::Type lease_type,
  784. const isc::asiolink::IOAddress& addr) const {
  785. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_ADDR6)
  786. .arg(addr.toText()).arg(lease_type);
  787. // Set up the WHERE clause value
  788. PsqlBindArray bind_array;
  789. // LEASE ADDRESS
  790. std::string addr_str = addr.toText();
  791. bind_array.add(addr_str);
  792. // LEASE_TYPE
  793. std::string type_str_ = boost::lexical_cast<std::string>(lease_type);
  794. bind_array.add(type_str_);
  795. // ... and get the data
  796. Lease6Ptr result;
  797. getLease(GET_LEASE6_ADDR, bind_array, result);
  798. return (result);
  799. }
  800. Lease6Collection
  801. PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid,
  802. uint32_t iaid) const {
  803. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  804. DHCPSRV_PGSQL_GET_IAID_DUID)
  805. .arg(iaid).arg(duid.toText()).arg(lease_type);
  806. // Set up the WHERE clause value
  807. PsqlBindArray bind_array;
  808. // DUID
  809. bind_array.add(duid.getDuid());
  810. // IAID
  811. std::string iaid_str = boost::lexical_cast<std::string>(iaid);
  812. bind_array.add(iaid_str);
  813. // LEASE_TYPE
  814. std::string lease_type_str = boost::lexical_cast<std::string>(lease_type);
  815. bind_array.add(lease_type_str);
  816. // ... and get the data
  817. Lease6Collection result;
  818. getLeaseCollection(GET_LEASE6_DUID_IAID, bind_array, result);
  819. return (result);
  820. }
  821. Lease6Collection
  822. PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid,
  823. uint32_t iaid, SubnetID subnet_id) const {
  824. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  825. DHCPSRV_PGSQL_GET_IAID_SUBID_DUID)
  826. .arg(iaid).arg(subnet_id).arg(duid.toText()).arg(lease_type);
  827. // Set up the WHERE clause value
  828. PsqlBindArray bind_array;
  829. // LEASE_TYPE
  830. std::string lease_type_str = boost::lexical_cast<std::string>(lease_type);
  831. bind_array.add(lease_type_str);
  832. // DUID
  833. bind_array.add(duid.getDuid());
  834. // IAID
  835. std::string iaid_str = boost::lexical_cast<std::string>(iaid);
  836. bind_array.add(iaid_str);
  837. // SUBNET ID
  838. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  839. bind_array.add(subnet_id_str);
  840. // ... and get the data
  841. Lease6Collection result;
  842. getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, bind_array, result);
  843. return (result);
  844. }
  845. void
  846. PgSqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
  847. const size_t max_leases) const {
  848. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_EXPIRED6)
  849. .arg(max_leases);
  850. getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
  851. }
  852. void
  853. PgSqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
  854. const size_t max_leases) const {
  855. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_EXPIRED4)
  856. .arg(max_leases);
  857. getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
  858. }
  859. template<typename LeaseCollection>
  860. void
  861. PgSqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
  862. const size_t max_leases,
  863. StatementIndex statement_index) const {
  864. PsqlBindArray bind_array;
  865. // Exclude reclaimed leases.
  866. std::string state_str = boost::lexical_cast<std::string>(Lease::STATE_EXPIRED_RECLAIMED);
  867. bind_array.add(state_str);
  868. // Expiration timestamp.
  869. std::string timestamp_str = PgSqlLeaseExchange::convertToDatabaseTime(time(NULL));
  870. bind_array.add(timestamp_str);
  871. // If the number of leases is 0, we will return all leases. This is
  872. // achieved by setting the limit to a very high value.
  873. uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
  874. std::numeric_limits<uint32_t>::max();
  875. std::string limit_str = boost::lexical_cast<std::string>(limit);
  876. bind_array.add(limit_str);
  877. // Retrieve leases from the database.
  878. getLeaseCollection(statement_index, bind_array, expired_leases);
  879. }
  880. template<typename LeasePtr>
  881. void
  882. PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
  883. PsqlBindArray& bind_array,
  884. const LeasePtr& lease) {
  885. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  886. DHCPSRV_PGSQL_ADD_ADDR4).arg(tagged_statements[stindex].name);
  887. PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
  888. tagged_statements[stindex].nbparams,
  889. &bind_array.values_[0],
  890. &bind_array.lengths_[0],
  891. &bind_array.formats_[0], 0));
  892. conn_.checkStatementError(r, tagged_statements[stindex]);
  893. int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
  894. // Check success case first as it is the most likely outcome.
  895. if (affected_rows == 1) {
  896. return;
  897. }
  898. // If no rows affected, lease doesn't exist.
  899. if (affected_rows == 0) {
  900. isc_throw(NoSuchLease, "unable to update lease for address " <<
  901. lease->addr_.toText() << " as it does not exist");
  902. }
  903. // Should not happen - primary key constraint should only have selected
  904. // one row.
  905. isc_throw(DbOperationError, "apparently updated more than one lease "
  906. "that had the address " << lease->addr_.toText());
  907. }
  908. void
  909. PgSqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
  910. const StatementIndex stindex = UPDATE_LEASE4;
  911. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  912. DHCPSRV_PGSQL_UPDATE_ADDR4).arg(lease->addr_.toText());
  913. // Create the BIND array for the data being updated
  914. PsqlBindArray bind_array;
  915. exchange4_->createBindForSend(lease, bind_array);
  916. // Set up the WHERE clause and append it to the SQL_BIND array
  917. std::string addr4_ = boost::lexical_cast<std::string>
  918. (static_cast<uint32_t>(lease->addr_));
  919. bind_array.add(addr4_);
  920. // Drop to common update code
  921. updateLeaseCommon(stindex, bind_array, lease);
  922. }
  923. void
  924. PgSqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
  925. const StatementIndex stindex = UPDATE_LEASE6;
  926. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  927. DHCPSRV_PGSQL_UPDATE_ADDR6).arg(lease->addr_.toText());
  928. // Create the BIND array for the data being updated
  929. PsqlBindArray bind_array;
  930. exchange6_->createBindForSend(lease, bind_array);
  931. // Set up the WHERE clause and append it to the BIND array
  932. std::string addr_str = lease->addr_.toText();
  933. bind_array.add(addr_str);
  934. // Drop to common update code
  935. updateLeaseCommon(stindex, bind_array, lease);
  936. }
  937. uint64_t
  938. PgSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
  939. PsqlBindArray& bind_array) {
  940. PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
  941. tagged_statements[stindex].nbparams,
  942. &bind_array.values_[0],
  943. &bind_array.lengths_[0],
  944. &bind_array.formats_[0], 0));
  945. conn_.checkStatementError(r, tagged_statements[stindex]);
  946. int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
  947. return (affected_rows);
  948. }
  949. bool
  950. PgSqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
  951. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  952. DHCPSRV_PGSQL_DELETE_ADDR).arg(addr.toText());
  953. // Set up the WHERE clause value
  954. PsqlBindArray bind_array;
  955. if (addr.isV4()) {
  956. std::string addr4_str = boost::lexical_cast<std::string>
  957. (static_cast<uint32_t>(addr));
  958. bind_array.add(addr4_str);
  959. return (deleteLeaseCommon(DELETE_LEASE4, bind_array) > 0);
  960. }
  961. std::string addr6_str = addr.toText();
  962. bind_array.add(addr6_str);
  963. return (deleteLeaseCommon(DELETE_LEASE6, bind_array) > 0);
  964. }
  965. uint64_t
  966. PgSqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
  967. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  968. DHCPSRV_PGSQL_DELETE_EXPIRED_RECLAIMED4)
  969. .arg(secs);
  970. return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
  971. }
  972. uint64_t
  973. PgSqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
  974. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  975. DHCPSRV_PGSQL_DELETE_EXPIRED_RECLAIMED6)
  976. .arg(secs);
  977. return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
  978. }
  979. uint64_t
  980. PgSqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
  981. StatementIndex statement_index) {
  982. PsqlBindArray bind_array;
  983. // State is reclaimed.
  984. std::string state_str = boost::lexical_cast<std::string>(Lease::STATE_EXPIRED_RECLAIMED);
  985. bind_array.add(state_str);
  986. // Expiration timestamp.
  987. std::string expiration_str =
  988. PgSqlLeaseExchange::convertToDatabaseTime(time(NULL) - static_cast<time_t>(secs));
  989. bind_array.add(expiration_str);
  990. // Delete leases.
  991. return (deleteLeaseCommon(statement_index, bind_array));
  992. }
  993. string
  994. PgSqlLeaseMgr::getName() const {
  995. string name = "";
  996. try {
  997. name = conn_.getParameter("name");
  998. } catch (...) {
  999. // Return an empty name
  1000. }
  1001. return (name);
  1002. }
  1003. string
  1004. PgSqlLeaseMgr::getDescription() const {
  1005. return (string("PostgreSQL Database"));
  1006. }
  1007. pair<uint32_t, uint32_t>
  1008. PgSqlLeaseMgr::getVersion() const {
  1009. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1010. DHCPSRV_PGSQL_GET_VERSION);
  1011. PgSqlResult r(PQexecPrepared(conn_, "get_version", 0, NULL, NULL, NULL, 0));
  1012. conn_.checkStatementError(r, tagged_statements[GET_VERSION]);
  1013. istringstream tmp;
  1014. uint32_t version;
  1015. tmp.str(PQgetvalue(r, 0, 0));
  1016. tmp >> version;
  1017. tmp.str("");
  1018. tmp.clear();
  1019. uint32_t minor;
  1020. tmp.str(PQgetvalue(r, 0, 1));
  1021. tmp >> minor;
  1022. return make_pair<uint32_t, uint32_t>(version, minor);
  1023. }
  1024. void
  1025. PgSqlLeaseMgr::commit() {
  1026. conn_.commit();
  1027. }
  1028. void
  1029. PgSqlLeaseMgr::rollback() {
  1030. conn_.rollback();
  1031. }
  1032. }; // end of isc::dhcp namespace
  1033. }; // end of isc namespace