pgsql_lease_mgr.cc 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415
  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 accommodate hwaddr,hwtype, and hwaddr source
  23. /// columns. This is covered 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 },
  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. // RECOUNT_LEASE4_STATS,
  178. { 0, { OID_NONE },
  179. "recount_lease4_stats",
  180. "SELECT subnet_id, state, count(state) as state_count "
  181. "FROM lease4 GROUP BY subnet_id, state ORDER BY subnet_id"},
  182. // RECOUNT_LEASE6_STATS,
  183. { 0, { OID_NONE },
  184. "recount_lease6_stats",
  185. "SELECT subnet_id, lease_type, state, count(state) as state_count "
  186. "FROM lease6 GROUP BY subnet_id, lease_type, state "
  187. "ORDER BY subnet_id"},
  188. // End of list sentinel
  189. { 0, { 0 }, NULL, NULL}
  190. };
  191. };
  192. namespace isc {
  193. namespace dhcp {
  194. /// @brief Base class for marshalling leases to and from PostgreSQL.
  195. ///
  196. /// Provides the common functionality to set up binding information between
  197. /// lease objects in the program and their database representation in the
  198. /// database.
  199. class PgSqlLeaseExchange : public PgSqlExchange {
  200. public:
  201. PgSqlLeaseExchange()
  202. : addr_str_(""), valid_lifetime_(0), valid_lft_str_(""),
  203. expire_(0), expire_str_(""), subnet_id_(0), subnet_id_str_(""),
  204. cltt_(0), fqdn_fwd_(false), fqdn_rev_(false), hostname_(""),
  205. state_str_("") {
  206. }
  207. virtual ~PgSqlLeaseExchange(){}
  208. protected:
  209. /// @brief Common Instance members used for binding and conversion
  210. //@{
  211. std::string addr_str_;
  212. uint32_t valid_lifetime_;
  213. std::string valid_lft_str_;
  214. time_t expire_;
  215. std::string expire_str_;
  216. uint32_t subnet_id_;
  217. std::string subnet_id_str_;
  218. time_t cltt_;
  219. bool fqdn_fwd_;
  220. bool fqdn_rev_;
  221. std::string hostname_;
  222. std::string state_str_;
  223. //@}
  224. };
  225. /// @brief Supports exchanging IPv4 leases with PostgreSQL.
  226. class PgSqlLease4Exchange : public PgSqlLeaseExchange {
  227. private:
  228. /// @brief Column numbers for each column in the Lease4 table.
  229. /// These are used for both retrieving data and for looking up
  230. /// column labels for logging. Note that their numeric order
  231. /// MUST match that of the column order in the Lease4 table.
  232. static const size_t ADDRESS_COL = 0;
  233. static const size_t HWADDR_COL = 1;
  234. static const size_t CLIENT_ID_COL = 2;
  235. static const size_t VALID_LIFETIME_COL = 3;
  236. static const size_t EXPIRE_COL = 4;
  237. static const size_t SUBNET_ID_COL = 5;
  238. static const size_t FQDN_FWD_COL = 6;
  239. static const size_t FQDN_REV_COL = 7;
  240. static const size_t HOSTNAME_COL = 8;
  241. static const size_t STATE_COL = 9;
  242. /// @brief Number of columns in the table holding DHCPv4 leases.
  243. static const size_t LEASE_COLUMNS = 10;
  244. public:
  245. /// @brief Default constructor
  246. PgSqlLease4Exchange()
  247. : lease_(), addr4_(0), hwaddr_length_(0), hwaddr_(hwaddr_length_),
  248. client_id_length_(0) {
  249. BOOST_STATIC_ASSERT(9 < LEASE_COLUMNS);
  250. memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
  251. memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
  252. // Set the column names (for error messages)
  253. columns_.push_back("address");
  254. columns_.push_back("hwaddr");
  255. columns_.push_back("client_id");
  256. columns_.push_back("valid_lifetime");
  257. columns_.push_back("expire");
  258. columns_.push_back("subnet_id");
  259. columns_.push_back("fqdn_fwd");
  260. columns_.push_back("fqdn_rev");
  261. columns_.push_back("hostname");
  262. columns_.push_back("state");
  263. }
  264. /// @brief Creates the bind array for sending Lease4 data to the database.
  265. ///
  266. /// Converts each Lease4 member into the appropriate form and adds it
  267. /// to the bind array. Note that the array additions must occur in the
  268. /// order the columns are specified in the SQL statement. By convention
  269. /// all columns in the table are explicitly listed in the SQL statement(s)
  270. /// in the same order as they occur in the table.
  271. ///
  272. /// @param lease Lease4 object that is to be written to the database
  273. /// @param[out] bind_array array to populate with the lease data values
  274. ///
  275. /// @throw DbOperationError if bind_array cannot be populated.
  276. void createBindForSend(const Lease4Ptr& lease, PsqlBindArray& bind_array) {
  277. if (!lease) {
  278. isc_throw(BadValue, "createBindForSend:: Lease4 object is NULL");
  279. }
  280. // Store lease object to ensure it remains valid.
  281. lease_ = lease;
  282. try {
  283. addr_str_ = boost::lexical_cast<std::string>
  284. (lease->addr_.toUint32());
  285. bind_array.add(addr_str_);
  286. if (lease->hwaddr_ && !lease->hwaddr_->hwaddr_.empty()) {
  287. // PostgreSql does not provide MAX on variable length types
  288. // so we have to enforce it ourselves.
  289. if (lease->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
  290. isc_throw(DbOperationError, "Hardware address length : "
  291. << lease_->hwaddr_->hwaddr_.size()
  292. << " exceeds maximum allowed of: "
  293. << HWAddr::MAX_HWADDR_LEN);
  294. }
  295. bind_array.add(lease->hwaddr_->hwaddr_);
  296. } else {
  297. bind_array.add("");
  298. }
  299. if (lease->client_id_) {
  300. bind_array.add(lease->client_id_->getClientId());
  301. } else {
  302. bind_array.add("");
  303. }
  304. valid_lft_str_ = boost::lexical_cast<std::string>
  305. (lease->valid_lft_);
  306. bind_array.add(valid_lft_str_);
  307. expire_str_ = convertToDatabaseTime(lease->cltt_, lease_->valid_lft_);
  308. bind_array.add(expire_str_);
  309. subnet_id_str_ = boost::lexical_cast<std::string>
  310. (lease->subnet_id_);
  311. bind_array.add(subnet_id_str_);
  312. bind_array.add(lease->fqdn_fwd_);
  313. bind_array.add(lease->fqdn_rev_);
  314. bind_array.add(lease->hostname_);
  315. state_str_ = boost::lexical_cast<std::string>(lease->state_);
  316. bind_array.add(state_str_);
  317. } catch (const std::exception& ex) {
  318. isc_throw(DbOperationError,
  319. "Could not create bind array from Lease4: "
  320. << lease_->addr_.toText() << ", reason: " << ex.what());
  321. }
  322. }
  323. /// @brief Creates a Lease4 object from a given row in a result set.
  324. ///
  325. /// @param r result set containing one or rows from the Lease4 table
  326. /// @param row row number within the result set from to create the Lease4
  327. /// object.
  328. ///
  329. /// @return Lease4Ptr to the newly created Lease4 object
  330. /// @throw DbOperationError if the lease cannot be created.
  331. Lease4Ptr convertFromDatabase(const PgSqlResult& r, int row) {
  332. try {
  333. getColumnValue(r, row, ADDRESS_COL, addr4_);
  334. convertFromBytea(r, row, HWADDR_COL, hwaddr_buffer_,
  335. sizeof(hwaddr_buffer_), hwaddr_length_);
  336. convertFromBytea(r, row, CLIENT_ID_COL, client_id_buffer_,
  337. sizeof(client_id_buffer_), client_id_length_);
  338. getColumnValue(r, row, VALID_LIFETIME_COL, valid_lifetime_);
  339. expire_ = convertFromDatabaseTime(getRawColumnValue(r, row,
  340. EXPIRE_COL));
  341. getColumnValue(r, row , SUBNET_ID_COL, subnet_id_);
  342. cltt_ = expire_ - valid_lifetime_;
  343. getColumnValue(r, row, FQDN_FWD_COL, fqdn_fwd_);
  344. getColumnValue(r, row, FQDN_REV_COL, fqdn_rev_);
  345. hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
  346. uint32_t state;
  347. getColumnValue(r, row , STATE_COL, state);
  348. HWAddrPtr hwaddr(new HWAddr(hwaddr_buffer_, hwaddr_length_,
  349. HTYPE_ETHER));
  350. Lease4Ptr result(new Lease4(addr4_, hwaddr,
  351. client_id_buffer_, client_id_length_,
  352. valid_lifetime_, 0, 0, cltt_,
  353. subnet_id_, fqdn_fwd_, fqdn_rev_,
  354. hostname_));
  355. result->state_ = state;
  356. return (result);
  357. } catch (const std::exception& ex) {
  358. isc_throw(DbOperationError,
  359. "Could not convert data to Lease4, reason: "
  360. << ex.what());
  361. }
  362. }
  363. private:
  364. /// @brief Lease4 object currently being sent to the database.
  365. /// Storing this value ensures that it remains in scope while any bindings
  366. /// that refer to its contents are in use.
  367. Lease4Ptr lease_;
  368. /// @Brief Lease4 specific members used for binding and conversion.
  369. uint32_t addr4_;
  370. size_t hwaddr_length_;
  371. std::vector<uint8_t> hwaddr_;
  372. uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
  373. size_t client_id_length_;
  374. uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
  375. };
  376. /// @brief Supports exchanging IPv6 leases with PostgreSQL.
  377. class PgSqlLease6Exchange : public PgSqlLeaseExchange {
  378. private:
  379. /// @brief Column numbers for each column in the Lease6 table.
  380. /// These are used for both retrieving data and for looking up
  381. /// column labels for logging. Note that their numeric order
  382. /// MUST match that of the column order in the Lease6 table.
  383. //@{
  384. static const int ADDRESS_COL = 0;
  385. static const int DUID_COL = 1;
  386. static const int VALID_LIFETIME_COL = 2;
  387. static const int EXPIRE_COL = 3;
  388. static const int SUBNET_ID_COL = 4;
  389. static const int PREF_LIFETIME_COL = 5;
  390. static const int LEASE_TYPE_COL = 6;
  391. static const int IAID_COL = 7;
  392. static const int PREFIX_LEN_COL = 8;
  393. static const int FQDN_FWD_COL = 9;
  394. static const int FQDN_REV_COL = 10;
  395. static const int HOSTNAME_COL = 11;
  396. static const int STATE_COL = 12;
  397. //@}
  398. /// @brief Number of columns in the table holding DHCPv6 leases.
  399. static const size_t LEASE_COLUMNS = 13;
  400. public:
  401. PgSqlLease6Exchange()
  402. : lease_(), duid_length_(0), duid_(), iaid_u_(0), iaid_str_(""),
  403. lease_type_(Lease6::TYPE_NA), lease_type_str_(""), prefix_len_(0),
  404. prefix_len_str_(""), pref_lifetime_(0), preferred_lft_str_("") {
  405. BOOST_STATIC_ASSERT(12 < LEASE_COLUMNS);
  406. memset(duid_buffer_, 0, sizeof(duid_buffer_));
  407. // Set the column names (for error messages)
  408. columns_.push_back("address");
  409. columns_.push_back("duid");
  410. columns_.push_back("valid_lifetime");
  411. columns_.push_back("expire");
  412. columns_.push_back("subnet_id");
  413. columns_.push_back("pref_lifetime");
  414. columns_.push_back("lease_type");
  415. columns_.push_back("iaid");
  416. columns_.push_back("prefix_len");
  417. columns_.push_back("fqdn_fwd");
  418. columns_.push_back("fqdn_rev");
  419. columns_.push_back("hostname");
  420. columns_.push_back("state");
  421. }
  422. /// @brief Creates the bind array for sending Lease6 data to the database.
  423. ///
  424. /// Converts each Lease6 member into the appropriate form and adds it
  425. /// to the bind array. Note that the array additions must occur in the
  426. /// order the columns are specified in the SQL statement. By convention
  427. /// all columns in the table are explicitly listed in the SQL statement(s)
  428. /// in the same order as they occur in the table.
  429. ///
  430. /// @param lease Lease6 object that is to be written to the database
  431. /// @param[out] bind_array array to populate with the lease data values
  432. ///
  433. /// @throw DbOperationError if bind_array cannot be populated.
  434. void createBindForSend(const Lease6Ptr& lease, PsqlBindArray& bind_array) {
  435. if (!lease) {
  436. isc_throw(BadValue, "createBindForSend:: Lease6 object is NULL");
  437. }
  438. // Store lease object to ensure it remains valid.
  439. lease_ = lease;
  440. try {
  441. addr_str_ = lease_->addr_.toText();
  442. bind_array.add(addr_str_);
  443. if (lease_->duid_) {
  444. bind_array.add(lease_->duid_->getDuid());
  445. } else {
  446. isc_throw (BadValue, "IPv6 Lease cannot have a null DUID");
  447. }
  448. valid_lft_str_ = boost::lexical_cast<std::string>
  449. (lease->valid_lft_);
  450. bind_array.add(valid_lft_str_);
  451. expire_str_ = convertToDatabaseTime(lease->cltt_, lease_->valid_lft_);
  452. bind_array.add(expire_str_);
  453. subnet_id_str_ = boost::lexical_cast<std::string>
  454. (lease->subnet_id_);
  455. bind_array.add(subnet_id_str_);
  456. preferred_lft_str_ = boost::lexical_cast<std::string>
  457. (lease_->preferred_lft_);
  458. bind_array.add(preferred_lft_str_);
  459. lease_type_str_ = boost::lexical_cast<std::string>(lease_->type_);
  460. bind_array.add(lease_type_str_);
  461. // The iaid is stored as an INT in lease6 table, so we must
  462. // lexically cast from an integer version to avoid out of range
  463. // exception failure upon insert.
  464. iaid_u_.uval_ = lease_->iaid_;
  465. iaid_str_ = boost::lexical_cast<std::string>(iaid_u_.ival_);
  466. bind_array.add(iaid_str_);
  467. prefix_len_str_ = boost::lexical_cast<std::string>
  468. (static_cast<unsigned int>(lease_->prefixlen_));
  469. bind_array.add(prefix_len_str_);
  470. bind_array.add(lease->fqdn_fwd_);
  471. bind_array.add(lease->fqdn_rev_);
  472. bind_array.add(lease->hostname_);
  473. state_str_ = boost::lexical_cast<std::string>(lease->state_);
  474. bind_array.add(state_str_);
  475. } catch (const std::exception& ex) {
  476. isc_throw(DbOperationError,
  477. "Could not create bind array from Lease6: "
  478. << lease_->addr_.toText() << ", reason: " << ex.what());
  479. }
  480. }
  481. /// @brief Creates a Lease6 object from a given row in a result set.
  482. ///
  483. /// @param r result set containing one or rows from the Lease6 table
  484. /// @param row row number within the result set from to create the Lease6
  485. /// object.
  486. ///
  487. /// @return Lease6Ptr to the newly created Lease4 object
  488. /// @throw DbOperationError if the lease cannot be created.
  489. Lease6Ptr convertFromDatabase(const PgSqlResult& r, int row) {
  490. try {
  491. /// @todo In theory, an administrator could tweak lease
  492. /// information in the database. In this case, some of the
  493. /// values could be set to NULL. This is less likely than
  494. /// in case of host reservations, but we may consider if
  495. /// retrieved values should be checked for being NULL to
  496. /// prevent cryptic errors during conversions from NULL
  497. /// to actual values.
  498. isc::asiolink::IOAddress addr(getIPv6Value(r, row, ADDRESS_COL));
  499. convertFromBytea(r, row, DUID_COL, duid_buffer_,
  500. sizeof(duid_buffer_), duid_length_);
  501. DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
  502. getColumnValue(r, row, VALID_LIFETIME_COL, valid_lifetime_);
  503. expire_ = convertFromDatabaseTime(getRawColumnValue(r, row,
  504. EXPIRE_COL));
  505. cltt_ = expire_ - valid_lifetime_;
  506. getColumnValue(r, row , SUBNET_ID_COL, subnet_id_);
  507. getColumnValue(r, row , PREF_LIFETIME_COL, pref_lifetime_);
  508. getLeaseTypeColumnValue(r, row, LEASE_TYPE_COL, lease_type_);
  509. getColumnValue(r, row , IAID_COL, iaid_u_.ival_);
  510. getColumnValue(r, row , PREFIX_LEN_COL, prefix_len_);
  511. getColumnValue(r, row, FQDN_FWD_COL, fqdn_fwd_);
  512. getColumnValue(r, row, FQDN_REV_COL, fqdn_rev_);
  513. hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
  514. uint32_t state;
  515. getColumnValue(r, row , STATE_COL, state);
  516. /// @todo: implement this in #3557.
  517. HWAddrPtr hwaddr;
  518. Lease6Ptr result(new Lease6(lease_type_, addr, duid_ptr,
  519. iaid_u_.uval_, pref_lifetime_,
  520. valid_lifetime_, 0, 0,
  521. subnet_id_, fqdn_fwd_, fqdn_rev_,
  522. hostname_, hwaddr, prefix_len_));
  523. result->cltt_ = cltt_;
  524. result->state_ = state;
  525. return (result);
  526. } catch (const std::exception& ex) {
  527. isc_throw(DbOperationError,
  528. "Could not convert data to Lease6, reason: "
  529. << ex.what());
  530. }
  531. }
  532. /// @brief Fetches an integer text column as a Lease6::Type
  533. ///
  534. /// @param r the result set containing the query results
  535. /// @param row the row number within the result set
  536. /// @param col the column number within the row
  537. /// @param[out] value parameter to receive the converted value
  538. ///
  539. /// Note we depart from overloading getColumnValue to avoid ambiguity
  540. /// with base class methods for integers.
  541. ///
  542. /// @throw DbOperationError if the value cannot be fetched or is
  543. /// invalid.
  544. void getLeaseTypeColumnValue(const PgSqlResult& r, const int row,
  545. const size_t col, Lease6::Type& value) const {
  546. uint32_t raw_value = 0;
  547. getColumnValue(r, row , col, raw_value);
  548. switch (raw_value) {
  549. case Lease6::TYPE_NA:
  550. case Lease6::TYPE_TA:
  551. case Lease6::TYPE_PD:
  552. value = static_cast<Lease6::Type>(raw_value);
  553. break;
  554. default:
  555. isc_throw(DbOperationError, "Invalid lease type: " << raw_value
  556. << " for: " << getColumnLabel(r, col) << " row:" << row);
  557. }
  558. }
  559. private:
  560. /// @brief Lease6 object currently being sent to the database.
  561. /// Storing this value ensures that it remains in scope while any bindings
  562. /// that refer to its contents are in use.
  563. Lease6Ptr lease_;
  564. /// @brief Lease6 specific members for binding and conversion.
  565. //@{
  566. size_t duid_length_;
  567. vector<uint8_t> duid_;
  568. uint8_t duid_buffer_[DUID::MAX_DUID_LEN];
  569. /// @brief Union for marshalling IAID into and out of the database
  570. /// IAID is defined in the RFC as 4 octets, which Kea code handles as
  571. /// a uint32_t. Postgresql however, offers only signed integer types
  572. /// of sizes 2, 4, and 8 bytes (SMALLINT, INT, and BIGINT respectively).
  573. /// IAID is used in several indexes so rather than use the BIGINT, we
  574. /// use this union to safely move the value into and out of an INT column.
  575. union Uiaid {
  576. Uiaid(uint32_t val) : uval_(val){};
  577. Uiaid(int32_t val) : ival_(val){};
  578. uint32_t uval_;
  579. int32_t ival_;
  580. } iaid_u_;
  581. std::string iaid_str_;
  582. Lease6::Type lease_type_;
  583. std::string lease_type_str_;
  584. uint8_t prefix_len_;
  585. std::string prefix_len_str_;
  586. uint32_t pref_lifetime_;
  587. std::string preferred_lft_str_;
  588. //@}
  589. };
  590. /// @brief Base PgSql derivation of the statistical lease data query
  591. ///
  592. /// This class provides the functionality such as results storage and row
  593. /// fetching common to fulfilling the statistical lease data query.
  594. ///
  595. class PgSqlLeaseStatsQuery : public LeaseStatsQuery {
  596. public:
  597. /// @brief Constructor
  598. ///
  599. /// @param conn A open connection to the database housing the lease data
  600. /// @param statement The lease data SQL prepared statement to execute
  601. /// @param fetch_type Indicates whether or not lease_type should be
  602. /// fetched from the result set
  603. PgSqlLeaseStatsQuery(PgSqlConnection& conn, PgSqlTaggedStatement& statement,
  604. const bool fetch_type)
  605. : conn_(conn), statement_(statement), result_set_(), next_row_(0),
  606. fetch_type_(fetch_type) {
  607. }
  608. /// @brief Destructor
  609. virtual ~PgSqlLeaseStatsQuery() {};
  610. /// @brief Creates the lease statistical data result set
  611. ///
  612. /// The result set is populated by executing a prepared SQL query
  613. /// against the database which sums the leases per lease state per
  614. /// subnet id.
  615. void start() {
  616. // The query has no parameters, so we only need it's name.
  617. result_set_.reset(new PgSqlResult(PQexecPrepared(conn_, statement_.name,
  618. 0, NULL, NULL, NULL, 0)));
  619. conn_.checkStatementError(*result_set_, statement_);
  620. }
  621. /// @brief Fetches the next row in the result set
  622. ///
  623. /// Once the internal result set has been populated by invoking the
  624. /// the start() method, this method is used to iterate over the
  625. /// result set rows. Once the last row has been fetched, subsequent
  626. /// calls will return false.
  627. ///
  628. /// @param row Storage for the fetched row
  629. ///
  630. /// @return True if the fetch succeeded, false if there are no more
  631. /// rows to fetch.
  632. bool getNextRow(LeaseStatsRow& row) {
  633. // If we're past the end, punt.
  634. if (next_row_ >= result_set_->getRows()) {
  635. return (false);
  636. }
  637. // Fetch the subnet id.
  638. uint32_t col = 0;
  639. uint32_t subnet_id;
  640. PgSqlExchange::getColumnValue(*result_set_, next_row_, col, subnet_id);
  641. row.subnet_id_ = static_cast<SubnetID>(subnet_id);
  642. ++col;
  643. // Fetch the lease type if we were told to do so.
  644. if (fetch_type_) {
  645. uint32_t lease_type;
  646. PgSqlExchange::getColumnValue(*result_set_, next_row_ , col,
  647. lease_type);
  648. row.lease_type_ = static_cast<Lease::Type>(lease_type);
  649. ++col;
  650. } else {
  651. row.lease_type_ = Lease::TYPE_NA;
  652. }
  653. // Fetch the lease state.
  654. PgSqlExchange::getColumnValue(*result_set_, next_row_ , col,
  655. row.lease_state_);
  656. ++col;
  657. // Fetch the state count.
  658. PgSqlExchange::getColumnValue(*result_set_, next_row_, col,
  659. row.state_count_);
  660. // Point to the next row.
  661. ++next_row_;
  662. return (true);
  663. }
  664. protected:
  665. /// @brief Database connection to use to execute the query
  666. PgSqlConnection& conn_;
  667. /// @brief The query's prepared statement
  668. PgSqlTaggedStatement& statement_;
  669. /// @brief The result set returned by Postgres.
  670. boost::shared_ptr<PgSqlResult> result_set_;
  671. /// @brief Index of the next row to fetch
  672. uint32_t next_row_;
  673. /// @brief Indicates if query supplies lease type
  674. bool fetch_type_;
  675. };
  676. PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
  677. : LeaseMgr(), exchange4_(new PgSqlLease4Exchange()),
  678. exchange6_(new PgSqlLease6Exchange()), conn_(parameters) {
  679. conn_.openDatabase();
  680. int i = 0;
  681. for( ; tagged_statements[i].text != NULL ; ++i) {
  682. conn_.prepareStatement(tagged_statements[i]);
  683. }
  684. // Just in case somebody foo-barred things
  685. if (i != NUM_STATEMENTS) {
  686. isc_throw(DbOpenError, "Number of statements prepared: " << i
  687. << " does not match expected count:" << NUM_STATEMENTS);
  688. }
  689. pair<uint32_t, uint32_t> code_version(PG_SCHEMA_VERSION_MAJOR, PG_SCHEMA_VERSION_MINOR);
  690. pair<uint32_t, uint32_t> db_version = getVersion();
  691. if (code_version != db_version) {
  692. isc_throw(DbOpenError,
  693. "PostgreSQL schema version mismatch: need version: "
  694. << code_version.first << "." << code_version.second
  695. << " found version: " << db_version.first << "."
  696. << db_version.second);
  697. }
  698. }
  699. PgSqlLeaseMgr::~PgSqlLeaseMgr() {
  700. }
  701. std::string
  702. PgSqlLeaseMgr::getDBVersion() {
  703. std::stringstream tmp;
  704. tmp << "PostgreSQL backend " << PG_SCHEMA_VERSION_MAJOR;
  705. tmp << "." << PG_SCHEMA_VERSION_MINOR;
  706. tmp << ", library " << PQlibVersion();
  707. return (tmp.str());
  708. }
  709. bool
  710. PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
  711. PsqlBindArray& bind_array) {
  712. PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
  713. tagged_statements[stindex].nbparams,
  714. &bind_array.values_[0],
  715. &bind_array.lengths_[0],
  716. &bind_array.formats_[0], 0));
  717. int s = PQresultStatus(r);
  718. if (s != PGRES_COMMAND_OK) {
  719. // Failure: check for the special case of duplicate entry. If this is
  720. // the case, we return false to indicate that the row was not added.
  721. // Otherwise we throw an exception.
  722. if (conn_.compareError(r, PgSqlConnection::DUPLICATE_KEY)) {
  723. return (false);
  724. }
  725. conn_.checkStatementError(r, tagged_statements[stindex]);
  726. }
  727. return (true);
  728. }
  729. bool
  730. PgSqlLeaseMgr::addLease(const Lease4Ptr& lease) {
  731. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  732. DHCPSRV_PGSQL_ADD_ADDR4).arg(lease->addr_.toText());
  733. PsqlBindArray bind_array;
  734. exchange4_->createBindForSend(lease, bind_array);
  735. return (addLeaseCommon(INSERT_LEASE4, bind_array));
  736. }
  737. bool
  738. PgSqlLeaseMgr::addLease(const Lease6Ptr& lease) {
  739. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  740. DHCPSRV_PGSQL_ADD_ADDR6).arg(lease->addr_.toText());
  741. PsqlBindArray bind_array;
  742. exchange6_->createBindForSend(lease, bind_array);
  743. return (addLeaseCommon(INSERT_LEASE6, bind_array));
  744. }
  745. template <typename Exchange, typename LeaseCollection>
  746. void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
  747. PsqlBindArray& bind_array,
  748. Exchange& exchange,
  749. LeaseCollection& result,
  750. bool single) const {
  751. PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
  752. tagged_statements[stindex].nbparams,
  753. &bind_array.values_[0],
  754. &bind_array.lengths_[0],
  755. &bind_array.formats_[0], 0));
  756. conn_.checkStatementError(r, tagged_statements[stindex]);
  757. int rows = PQntuples(r);
  758. if (single && rows > 1) {
  759. isc_throw(MultipleRecords, "multiple records were found in the "
  760. "database where only one was expected for query "
  761. << tagged_statements[stindex].name);
  762. }
  763. for(int i = 0; i < rows; ++ i) {
  764. result.push_back(exchange->convertFromDatabase(r, i));
  765. }
  766. }
  767. void
  768. PgSqlLeaseMgr::getLease(StatementIndex stindex, PsqlBindArray& bind_array,
  769. Lease4Ptr& result) const {
  770. // Create appropriate collection object and get all leases matching
  771. // the selection criteria. The "single" parameter is true to indicate
  772. // that the called method should throw an exception if multiple
  773. // matching records are found: this particular method is called when only
  774. // one or zero matches is expected.
  775. Lease4Collection collection;
  776. getLeaseCollection(stindex, bind_array, exchange4_, collection, true);
  777. // Return single record if present, else clear the lease.
  778. if (collection.empty()) {
  779. result.reset();
  780. } else {
  781. result = *collection.begin();
  782. }
  783. }
  784. void
  785. PgSqlLeaseMgr::getLease(StatementIndex stindex, PsqlBindArray& bind_array,
  786. Lease6Ptr& result) const {
  787. // Create appropriate collection object and get all leases matching
  788. // the selection criteria. The "single" parameter is true to indicate
  789. // that the called method should throw an exception if multiple
  790. // matching records are found: this particular method is called when only
  791. // one or zero matches is expected.
  792. Lease6Collection collection;
  793. getLeaseCollection(stindex, bind_array, exchange6_, collection, true);
  794. // Return single record if present, else clear the lease.
  795. if (collection.empty()) {
  796. result.reset();
  797. } else {
  798. result = *collection.begin();
  799. }
  800. }
  801. Lease4Ptr
  802. PgSqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
  803. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  804. DHCPSRV_PGSQL_GET_ADDR4).arg(addr.toText());
  805. // Set up the WHERE clause value
  806. PsqlBindArray bind_array;
  807. // LEASE ADDRESS
  808. std::string addr_str = boost::lexical_cast<std::string>
  809. (addr.toUint32());
  810. bind_array.add(addr_str);
  811. // Get the data
  812. Lease4Ptr result;
  813. getLease(GET_LEASE4_ADDR, bind_array, result);
  814. return (result);
  815. }
  816. Lease4Collection
  817. PgSqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
  818. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  819. DHCPSRV_PGSQL_GET_HWADDR).arg(hwaddr.toText());
  820. // Set up the WHERE clause value
  821. PsqlBindArray bind_array;
  822. // HWADDR
  823. if (!hwaddr.hwaddr_.empty()) {
  824. bind_array.add(hwaddr.hwaddr_);
  825. } else {
  826. bind_array.add("");
  827. }
  828. // Get the data
  829. Lease4Collection result;
  830. getLeaseCollection(GET_LEASE4_HWADDR, bind_array, result);
  831. return (result);
  832. }
  833. Lease4Ptr
  834. PgSqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
  835. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  836. DHCPSRV_PGSQL_GET_SUBID_HWADDR)
  837. .arg(subnet_id).arg(hwaddr.toText());
  838. // Set up the WHERE clause value
  839. PsqlBindArray bind_array;
  840. // HWADDR
  841. if (!hwaddr.hwaddr_.empty()) {
  842. bind_array.add(hwaddr.hwaddr_);
  843. } else {
  844. bind_array.add("");
  845. }
  846. // SUBNET_ID
  847. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  848. bind_array.add(subnet_id_str);
  849. // Get the data
  850. Lease4Ptr result;
  851. getLease(GET_LEASE4_HWADDR_SUBID, bind_array, result);
  852. return (result);
  853. }
  854. Lease4Collection
  855. PgSqlLeaseMgr::getLease4(const ClientId& clientid) const {
  856. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  857. DHCPSRV_PGSQL_GET_CLIENTID).arg(clientid.toText());
  858. // Set up the WHERE clause value
  859. PsqlBindArray bind_array;
  860. // CLIENT_ID
  861. bind_array.add(clientid.getClientId());
  862. // Get the data
  863. Lease4Collection result;
  864. getLeaseCollection(GET_LEASE4_CLIENTID, bind_array, result);
  865. return (result);
  866. }
  867. Lease4Ptr
  868. PgSqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
  869. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  870. DHCPSRV_PGSQL_GET_SUBID_CLIENTID)
  871. .arg(subnet_id).arg(clientid.toText());
  872. // Set up the WHERE clause value
  873. PsqlBindArray bind_array;
  874. // CLIENT_ID
  875. bind_array.add(clientid.getClientId());
  876. // SUBNET_ID
  877. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  878. bind_array.add(subnet_id_str);
  879. // Get the data
  880. Lease4Ptr result;
  881. getLease(GET_LEASE4_CLIENTID_SUBID, bind_array, result);
  882. return (result);
  883. }
  884. Lease4Ptr
  885. PgSqlLeaseMgr::getLease4(const ClientId&, const HWAddr&, SubnetID) const {
  886. /// This function is currently not implemented because allocation engine
  887. /// searches for the lease using HW address or client identifier.
  888. /// It never uses both parameters in the same time. We need to
  889. /// consider if this function is needed at all.
  890. isc_throw(NotImplemented, "The PgSqlLeaseMgr::getLease4 function was"
  891. " called, but it is not implemented");
  892. }
  893. Lease6Ptr
  894. PgSqlLeaseMgr::getLease6(Lease::Type lease_type,
  895. const isc::asiolink::IOAddress& addr) const {
  896. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_ADDR6)
  897. .arg(addr.toText()).arg(lease_type);
  898. // Set up the WHERE clause value
  899. PsqlBindArray bind_array;
  900. // LEASE ADDRESS
  901. std::string addr_str = addr.toText();
  902. bind_array.add(addr_str);
  903. // LEASE_TYPE
  904. std::string type_str_ = boost::lexical_cast<std::string>(lease_type);
  905. bind_array.add(type_str_);
  906. // ... and get the data
  907. Lease6Ptr result;
  908. getLease(GET_LEASE6_ADDR, bind_array, result);
  909. return (result);
  910. }
  911. Lease6Collection
  912. PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid,
  913. uint32_t iaid) const {
  914. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  915. DHCPSRV_PGSQL_GET_IAID_DUID)
  916. .arg(iaid).arg(duid.toText()).arg(lease_type);
  917. // Set up the WHERE clause value
  918. PsqlBindArray bind_array;
  919. // DUID
  920. bind_array.add(duid.getDuid());
  921. // IAID
  922. std::string iaid_str = boost::lexical_cast<std::string>(iaid);
  923. bind_array.add(iaid_str);
  924. // LEASE_TYPE
  925. std::string lease_type_str = boost::lexical_cast<std::string>(lease_type);
  926. bind_array.add(lease_type_str);
  927. // ... and get the data
  928. Lease6Collection result;
  929. getLeaseCollection(GET_LEASE6_DUID_IAID, bind_array, result);
  930. return (result);
  931. }
  932. Lease6Collection
  933. PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid,
  934. uint32_t iaid, SubnetID subnet_id) const {
  935. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  936. DHCPSRV_PGSQL_GET_IAID_SUBID_DUID)
  937. .arg(iaid).arg(subnet_id).arg(duid.toText()).arg(lease_type);
  938. // Set up the WHERE clause value
  939. PsqlBindArray bind_array;
  940. // LEASE_TYPE
  941. std::string lease_type_str = boost::lexical_cast<std::string>(lease_type);
  942. bind_array.add(lease_type_str);
  943. // DUID
  944. bind_array.add(duid.getDuid());
  945. // IAID
  946. std::string iaid_str = boost::lexical_cast<std::string>(iaid);
  947. bind_array.add(iaid_str);
  948. // SUBNET ID
  949. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  950. bind_array.add(subnet_id_str);
  951. // ... and get the data
  952. Lease6Collection result;
  953. getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, bind_array, result);
  954. return (result);
  955. }
  956. void
  957. PgSqlLeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
  958. const size_t max_leases) const {
  959. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_EXPIRED6)
  960. .arg(max_leases);
  961. getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE6_EXPIRE);
  962. }
  963. void
  964. PgSqlLeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
  965. const size_t max_leases) const {
  966. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_EXPIRED4)
  967. .arg(max_leases);
  968. getExpiredLeasesCommon(expired_leases, max_leases, GET_LEASE4_EXPIRE);
  969. }
  970. template<typename LeaseCollection>
  971. void
  972. PgSqlLeaseMgr::getExpiredLeasesCommon(LeaseCollection& expired_leases,
  973. const size_t max_leases,
  974. StatementIndex statement_index) const {
  975. PsqlBindArray bind_array;
  976. // Exclude reclaimed leases.
  977. std::string state_str = boost::lexical_cast<std::string>(Lease::STATE_EXPIRED_RECLAIMED);
  978. bind_array.add(state_str);
  979. // Expiration timestamp.
  980. std::string timestamp_str = PgSqlLeaseExchange::convertToDatabaseTime(time(NULL));
  981. bind_array.add(timestamp_str);
  982. // If the number of leases is 0, we will return all leases. This is
  983. // achieved by setting the limit to a very high value.
  984. uint32_t limit = max_leases > 0 ? static_cast<uint32_t>(max_leases) :
  985. std::numeric_limits<uint32_t>::max();
  986. std::string limit_str = boost::lexical_cast<std::string>(limit);
  987. bind_array.add(limit_str);
  988. // Retrieve leases from the database.
  989. getLeaseCollection(statement_index, bind_array, expired_leases);
  990. }
  991. template<typename LeasePtr>
  992. void
  993. PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
  994. PsqlBindArray& bind_array,
  995. const LeasePtr& lease) {
  996. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  997. DHCPSRV_PGSQL_ADD_ADDR4).arg(tagged_statements[stindex].name);
  998. PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
  999. tagged_statements[stindex].nbparams,
  1000. &bind_array.values_[0],
  1001. &bind_array.lengths_[0],
  1002. &bind_array.formats_[0], 0));
  1003. conn_.checkStatementError(r, tagged_statements[stindex]);
  1004. int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
  1005. // Check success case first as it is the most likely outcome.
  1006. if (affected_rows == 1) {
  1007. return;
  1008. }
  1009. // If no rows affected, lease doesn't exist.
  1010. if (affected_rows == 0) {
  1011. isc_throw(NoSuchLease, "unable to update lease for address " <<
  1012. lease->addr_.toText() << " as it does not exist");
  1013. }
  1014. // Should not happen - primary key constraint should only have selected
  1015. // one row.
  1016. isc_throw(DbOperationError, "apparently updated more than one lease "
  1017. "that had the address " << lease->addr_.toText());
  1018. }
  1019. void
  1020. PgSqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
  1021. const StatementIndex stindex = UPDATE_LEASE4;
  1022. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1023. DHCPSRV_PGSQL_UPDATE_ADDR4).arg(lease->addr_.toText());
  1024. // Create the BIND array for the data being updated
  1025. PsqlBindArray bind_array;
  1026. exchange4_->createBindForSend(lease, bind_array);
  1027. // Set up the WHERE clause and append it to the SQL_BIND array
  1028. std::string addr4_ = boost::lexical_cast<std::string>
  1029. (lease->addr_.toUint32());
  1030. bind_array.add(addr4_);
  1031. // Drop to common update code
  1032. updateLeaseCommon(stindex, bind_array, lease);
  1033. }
  1034. void
  1035. PgSqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
  1036. const StatementIndex stindex = UPDATE_LEASE6;
  1037. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1038. DHCPSRV_PGSQL_UPDATE_ADDR6).arg(lease->addr_.toText());
  1039. // Create the BIND array for the data being updated
  1040. PsqlBindArray bind_array;
  1041. exchange6_->createBindForSend(lease, bind_array);
  1042. // Set up the WHERE clause and append it to the BIND array
  1043. std::string addr_str = lease->addr_.toText();
  1044. bind_array.add(addr_str);
  1045. // Drop to common update code
  1046. updateLeaseCommon(stindex, bind_array, lease);
  1047. }
  1048. uint64_t
  1049. PgSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
  1050. PsqlBindArray& bind_array) {
  1051. PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
  1052. tagged_statements[stindex].nbparams,
  1053. &bind_array.values_[0],
  1054. &bind_array.lengths_[0],
  1055. &bind_array.formats_[0], 0));
  1056. conn_.checkStatementError(r, tagged_statements[stindex]);
  1057. int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
  1058. return (affected_rows);
  1059. }
  1060. bool
  1061. PgSqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
  1062. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1063. DHCPSRV_PGSQL_DELETE_ADDR).arg(addr.toText());
  1064. // Set up the WHERE clause value
  1065. PsqlBindArray bind_array;
  1066. if (addr.isV4()) {
  1067. std::string addr4_str = boost::lexical_cast<std::string>
  1068. (addr.toUint32());
  1069. bind_array.add(addr4_str);
  1070. return (deleteLeaseCommon(DELETE_LEASE4, bind_array) > 0);
  1071. }
  1072. std::string addr6_str = addr.toText();
  1073. bind_array.add(addr6_str);
  1074. return (deleteLeaseCommon(DELETE_LEASE6, bind_array) > 0);
  1075. }
  1076. uint64_t
  1077. PgSqlLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
  1078. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1079. DHCPSRV_PGSQL_DELETE_EXPIRED_RECLAIMED4)
  1080. .arg(secs);
  1081. return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE4_STATE_EXPIRED));
  1082. }
  1083. uint64_t
  1084. PgSqlLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
  1085. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1086. DHCPSRV_PGSQL_DELETE_EXPIRED_RECLAIMED6)
  1087. .arg(secs);
  1088. return (deleteExpiredReclaimedLeasesCommon(secs, DELETE_LEASE6_STATE_EXPIRED));
  1089. }
  1090. uint64_t
  1091. PgSqlLeaseMgr::deleteExpiredReclaimedLeasesCommon(const uint32_t secs,
  1092. StatementIndex statement_index) {
  1093. PsqlBindArray bind_array;
  1094. // State is reclaimed.
  1095. std::string state_str = boost::lexical_cast<std::string>(Lease::STATE_EXPIRED_RECLAIMED);
  1096. bind_array.add(state_str);
  1097. // Expiration timestamp.
  1098. std::string expiration_str =
  1099. PgSqlLeaseExchange::convertToDatabaseTime(time(NULL) - static_cast<time_t>(secs));
  1100. bind_array.add(expiration_str);
  1101. // Delete leases.
  1102. return (deleteLeaseCommon(statement_index, bind_array));
  1103. }
  1104. LeaseStatsQueryPtr
  1105. PgSqlLeaseMgr::startLeaseStatsQuery4() {
  1106. LeaseStatsQueryPtr query(
  1107. new PgSqlLeaseStatsQuery(conn_,
  1108. tagged_statements[RECOUNT_LEASE4_STATS],
  1109. false));
  1110. query->start();
  1111. return(query);
  1112. }
  1113. LeaseStatsQueryPtr
  1114. PgSqlLeaseMgr::startLeaseStatsQuery6() {
  1115. LeaseStatsQueryPtr query(
  1116. new PgSqlLeaseStatsQuery(conn_,
  1117. tagged_statements[RECOUNT_LEASE6_STATS],
  1118. true));
  1119. query->start();
  1120. return(query);
  1121. }
  1122. string
  1123. PgSqlLeaseMgr::getName() const {
  1124. string name = "";
  1125. try {
  1126. name = conn_.getParameter("name");
  1127. } catch (...) {
  1128. // Return an empty name
  1129. }
  1130. return (name);
  1131. }
  1132. string
  1133. PgSqlLeaseMgr::getDescription() const {
  1134. return (string("PostgreSQL Database"));
  1135. }
  1136. pair<uint32_t, uint32_t>
  1137. PgSqlLeaseMgr::getVersion() const {
  1138. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1139. DHCPSRV_PGSQL_GET_VERSION);
  1140. PgSqlResult r(PQexecPrepared(conn_, "get_version", 0, NULL, NULL, NULL, 0));
  1141. conn_.checkStatementError(r, tagged_statements[GET_VERSION]);
  1142. istringstream tmp;
  1143. uint32_t version;
  1144. tmp.str(PQgetvalue(r, 0, 0));
  1145. tmp >> version;
  1146. tmp.str("");
  1147. tmp.clear();
  1148. uint32_t minor;
  1149. tmp.str(PQgetvalue(r, 0, 1));
  1150. tmp >> minor;
  1151. return (make_pair(version, minor));
  1152. }
  1153. void
  1154. PgSqlLeaseMgr::commit() {
  1155. conn_.commit();
  1156. }
  1157. void
  1158. PgSqlLeaseMgr::rollback() {
  1159. conn_.rollback();
  1160. }
  1161. }; // end of isc::dhcp namespace
  1162. }; // end of isc namespace