pgsql_lease_mgr.cc 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567
  1. // Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <asiolink/io_address.h>
  16. #include <dhcp/duid.h>
  17. #include <dhcp/hwaddr.h>
  18. #include <dhcpsrv/dhcpsrv_log.h>
  19. #include <dhcpsrv/pgsql_lease_mgr.h>
  20. #include <boost/static_assert.hpp>
  21. #include <iostream>
  22. #include <iomanip>
  23. #include <sstream>
  24. #include <string>
  25. #include <time.h>
  26. // PostgreSQL errors should be tested based on the SQL state code. Each state
  27. // code is 5 decimal, ASCII, digits, the first two define the category of
  28. // error, the last three are the specific error. PostgreSQL makes the state
  29. // code as a char[5]. Macros for each code are defined in PostgreSQL's
  30. // errorcodes.h, although they require a second macro, MAKE_SQLSTATE for
  31. // completion. PostgreSQL deliberately omits this macro from errocodes.h
  32. // so callers can supply their own.
  33. #define MAKE_SQLSTATE(ch1,ch2,ch3,ch4,ch5) {ch1,ch2,ch3,ch4,ch5}
  34. #include <utils/errcodes.h>
  35. const size_t STATECODE_LEN = 5;
  36. // Currently the only one we care to look for is duplicate key.
  37. const char DUPLICATE_KEY[] = ERRCODE_UNIQUE_VIOLATION;
  38. using namespace isc;
  39. using namespace isc::dhcp;
  40. using namespace std;
  41. namespace {
  42. // Maximum number of parameters used in any single query
  43. const size_t MAX_PARAMETERS_IN_QUERY = 13;
  44. /// @brief Defines a single query
  45. struct TaggedStatement {
  46. /// Number of parameters for a given query
  47. int nbparams;
  48. /// @brief OID types
  49. ///
  50. /// Specify parameter types. See /usr/include/postgresql/catalog/pg_type.h.
  51. /// For some reason that header does not export those parameters.
  52. /// Those OIDs must match both input and output parameters.
  53. const Oid types[MAX_PARAMETERS_IN_QUERY];
  54. /// Short name of the query.
  55. const char* name;
  56. /// Text representation of the actual query.
  57. const char* text;
  58. };
  59. /// @brief Constants for PostgreSQL data types
  60. /// This are defined by PostreSQL in <catalog/pg_type.h>, but including
  61. /// this file is extrordinarily convoluted, so we'll use these to fill-in.
  62. const size_t OID_NONE = 0; // PostgreSQL infers proper type
  63. const size_t OID_BOOL = 16;
  64. const size_t OID_BYTEA = 17;
  65. const size_t OID_INT8 = 20; // 8 byte int
  66. const size_t OID_INT2 = 21; // 2 byte int
  67. const size_t OID_TIMESTAMP = 1114;
  68. const size_t OID_VARCHAR = 1043;
  69. /// @brief Catalog of all the SQL statements currently supported. Note
  70. /// that the order columns appear in statement body must match the order they
  71. /// that the occur in the table. This does not apply to the where clause.
  72. TaggedStatement tagged_statements[] = {
  73. // DELETE_LEASE4
  74. { 1, { OID_INT8 },
  75. "delete_lease4",
  76. "DELETE FROM lease4 WHERE address = $1"},
  77. // DELETE_LEASE6
  78. { 1, { OID_VARCHAR },
  79. "delete_lease6",
  80. "DELETE FROM lease6 WHERE address = $1"},
  81. // GET_LEASE4_ADDR
  82. { 1, { OID_INT8 },
  83. "get_lease4_addr",
  84. "SELECT address, hwaddr, client_id, "
  85. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  86. "fqdn_fwd, fqdn_rev, hostname "
  87. "FROM lease4 "
  88. "WHERE address = $1"},
  89. // GET_LEASE4_CLIENTID
  90. { 1, { OID_BYTEA },
  91. "get_lease4_clientid",
  92. "SELECT address, hwaddr, client_id, "
  93. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  94. "fqdn_fwd, fqdn_rev, hostname "
  95. "FROM lease4 "
  96. "WHERE client_id = $1"},
  97. // GET_LEASE4_CLIENTID_SUBID
  98. { 2, { OID_BYTEA, OID_INT8 },
  99. "get_lease4_clientid_subid",
  100. "SELECT address, hwaddr, client_id, "
  101. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  102. "fqdn_fwd, fqdn_rev, hostname "
  103. "FROM lease4 "
  104. "WHERE client_id = $1 AND subnet_id = $2"},
  105. // GET_LEASE4_HWADDR
  106. { 1, { OID_BYTEA },
  107. "get_lease4_hwaddr",
  108. "SELECT address, hwaddr, client_id, "
  109. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  110. "fqdn_fwd, fqdn_rev, hostname "
  111. "FROM lease4 "
  112. "WHERE hwaddr = $1"},
  113. // GET_LEASE4_HWADDR_SUBID
  114. { 2, { OID_BYTEA, OID_INT8 },
  115. "get_lease4_hwaddr_subid",
  116. "SELECT address, hwaddr, client_id, "
  117. "valid_lifetime, extract(epoch from expire)::bigint, subnet_id, "
  118. "fqdn_fwd, fqdn_rev, hostname "
  119. "FROM lease4 "
  120. "WHERE hwaddr = $1 AND subnet_id = $2"},
  121. // GET_LEASE6_ADDR
  122. { 2, { OID_VARCHAR, OID_INT2 },
  123. "get_lease6_addr",
  124. "SELECT address, duid, valid_lifetime, "
  125. "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
  126. "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
  127. "FROM lease6 "
  128. "WHERE address = $1 AND lease_type = $2"},
  129. // GET_LEASE6_DUID_IAID
  130. { 3, { OID_BYTEA, OID_INT8, OID_INT2 },
  131. "get_lease6_duid_iaid",
  132. "SELECT address, duid, valid_lifetime, "
  133. "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
  134. "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
  135. "FROM lease6 "
  136. "WHERE duid = $1 AND iaid = $2 AND lease_type = $3"},
  137. // GET_LEASE6_DUID_IAID_SUBID
  138. { 4, { OID_INT2, OID_BYTEA, OID_INT8, OID_INT8 },
  139. "get_lease6_duid_iaid_subid",
  140. "SELECT address, duid, valid_lifetime, "
  141. "extract(epoch from expire)::bigint, subnet_id, pref_lifetime, "
  142. "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname "
  143. "FROM lease6 "
  144. "WHERE lease_type = $1 "
  145. "AND duid = $2 AND iaid = $3 AND subnet_id = $4"},
  146. // GET_VERSION
  147. { 0, { OID_NONE },
  148. "get_version",
  149. "SELECT version, minor FROM schema_version"},
  150. // INSERT_LEASE4
  151. { 9, { OID_INT8, OID_BYTEA, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8,
  152. OID_BOOL, OID_BOOL, OID_VARCHAR },
  153. "insert_lease4",
  154. "INSERT INTO lease4(address, hwaddr, client_id, "
  155. "valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname) "
  156. "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)"},
  157. // INSERT_LEASE6
  158. { 12, { OID_VARCHAR, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8,
  159. OID_INT8, OID_INT2, OID_INT8, OID_INT2, OID_BOOL, OID_BOOL,
  160. OID_VARCHAR },
  161. "insert_lease6",
  162. "INSERT INTO lease6(address, duid, valid_lifetime, "
  163. "expire, subnet_id, pref_lifetime, "
  164. "lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname) "
  165. "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)"},
  166. // UPDATE_LEASE4
  167. { 10, { OID_INT8, OID_BYTEA, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8,
  168. OID_BOOL, OID_BOOL, OID_VARCHAR, OID_INT8 },
  169. "update_lease4",
  170. "UPDATE lease4 SET address = $1, hwaddr = $2, "
  171. "client_id = $3, valid_lifetime = $4, expire = $5, "
  172. "subnet_id = $6, fqdn_fwd = $7, fqdn_rev = $8, hostname = $9 "
  173. "WHERE address = $10"},
  174. // UPDATE_LEASE6
  175. { 13, { OID_VARCHAR, OID_BYTEA, OID_INT8, OID_TIMESTAMP, OID_INT8, OID_INT8,
  176. OID_INT2, OID_INT8, OID_INT2, OID_BOOL, OID_BOOL, OID_VARCHAR,
  177. OID_VARCHAR },
  178. "update_lease6",
  179. "UPDATE lease6 SET address = $1, duid = $2, "
  180. "valid_lifetime = $3, expire = $4, subnet_id = $5, "
  181. "pref_lifetime = $6, lease_type = $7, iaid = $8, "
  182. "prefix_len = $9, fqdn_fwd = $10, fqdn_rev = $11, hostname = $12 "
  183. "WHERE address = $13"},
  184. // End of list sentinel
  185. { 0, { 0 }, NULL, NULL}
  186. };
  187. };
  188. namespace isc {
  189. namespace dhcp {
  190. const int PsqlBindArray::TEXT_FMT = 0;
  191. const int PsqlBindArray::BINARY_FMT = 1;
  192. const char* PsqlBindArray::TRUE_STR = "TRUE";
  193. const char* PsqlBindArray::FALSE_STR = "FALSE";
  194. void PsqlBindArray::add(const char* value) {
  195. values_.push_back(value);
  196. lengths_.push_back(strlen(value));
  197. formats_.push_back(TEXT_FMT);
  198. }
  199. void PsqlBindArray::add(const std::string& value) {
  200. values_.push_back(value.c_str());
  201. lengths_.push_back(value.size());
  202. formats_.push_back(TEXT_FMT);
  203. }
  204. void PsqlBindArray::add(const std::vector<uint8_t>& data) {
  205. values_.push_back(reinterpret_cast<const char*>(&(data[0])));
  206. lengths_.push_back(data.size());
  207. formats_.push_back(BINARY_FMT);
  208. }
  209. void PsqlBindArray::add(const bool& value) {
  210. add(value ? TRUE_STR : FALSE_STR);
  211. }
  212. std::string PsqlBindArray::toText() {
  213. std::ostringstream stream;
  214. for (int i = 0; i < values_.size(); ++i) {
  215. stream << i << " : ";
  216. if (formats_[i] == TEXT_FMT) {
  217. stream << "\"" << values_[i] << "\"" << std::endl;
  218. } else {
  219. const char *data = values_[i];
  220. if (lengths_[i] == 0) {
  221. stream << "empty" << std::endl;
  222. } else {
  223. stream << "0x";
  224. for (int i = 0; i < lengths_[i]; ++i) {
  225. stream << setfill('0') << setw(2) << setbase(16)
  226. << static_cast<unsigned int>(data[i]);
  227. }
  228. stream << std::endl;
  229. }
  230. }
  231. }
  232. return (stream.str());
  233. }
  234. /// @brief Base class for marshalling leases to and from PostgreSQL.
  235. ///
  236. /// Provides the common functionality to set up binding information between
  237. /// lease objects in the program and their database representation in the
  238. /// database.
  239. class PgSqlLeaseExchange {
  240. public:
  241. PgSqlLeaseExchange()
  242. : addr_str_(""), valid_lifetime_(0), valid_lft_str_(""),
  243. expire_(0), expire_str_(""), subnet_id_(0), subnet_id_str_(""),
  244. cltt_(0), fqdn_fwd_(false), fqdn_rev_(false), hostname_("") {
  245. }
  246. virtual ~PgSqlLeaseExchange(){}
  247. /// @brief Converts lease expiration time to a text representation in
  248. /// local time.
  249. ///
  250. /// The expiration time is calculated as a sum of the cltt (client last
  251. /// transmit time) and the valid lifetime.
  252. ///
  253. /// The format of the output string is "%Y-%m-%d %H:%M:%S". Database
  254. /// table columns using this value should be typed as TIMESTAMP WITH
  255. /// TIME ZONE. For such columns PostgreSQL assumes input strings without
  256. /// timezones should be treated as in local time and are converted to UTC
  257. /// when stored. Likewise, these columns are automatically adjusted
  258. /// upon retrieval unless fetched via "extract(epoch from <column>))".
  259. ///
  260. /// @param cltt Client last transmit time
  261. /// @param valid_lifetime Valid lifetime
  262. ///
  263. /// @return std::string containing the stringified time
  264. /// @throw isc::BadValue if the sum of the calculated expiration time is
  265. /// greater than the value of @c LeaseMgr::MAX_DB_TIME.
  266. static std::string
  267. convertToDatabaseTime(const time_t cltt, const uint32_t valid_lifetime) {
  268. // Calculate expiry time. Store it in the 64-bit value so as we can detect
  269. // overflows.
  270. int64_t expire_time_64 = static_cast<int64_t>(cltt) +
  271. static_cast<int64_t>(valid_lifetime);
  272. // It has been observed that the PostgreSQL doesn't deal well with the
  273. // timestamp values beyond the LeaseMgr::MAX_DB_TIME seconds since the
  274. // beginning of the epoch (around year 2038). The value is often
  275. // stored in the database but it is invalid when read back (overflow?).
  276. // Hence, the maximum timestamp value is restricted here.
  277. if (expire_time_64 > LeaseMgr::MAX_DB_TIME) {
  278. isc_throw(isc::BadValue, "Time value is too large: " << expire_time_64);
  279. }
  280. struct tm tinfo;
  281. char buffer[20];
  282. const time_t time_val = static_cast<time_t>(expire_time_64);
  283. localtime_r(&time_val, &tinfo);
  284. strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tinfo);
  285. return (std::string(buffer));
  286. }
  287. /// @brief Converts time stamp from the database to a time_t
  288. ///
  289. /// @param db_time_val timestamp to be converted. This value
  290. /// is expected to be the number of seconds since the epoch
  291. /// expressed as base-10 integer string.
  292. /// @return Converted timestamp as time_t value.
  293. static time_t convertFromDatabaseTime(const std::string& db_time_val) {
  294. // Convert string time value to time_t
  295. try {
  296. return (boost::lexical_cast<time_t>(db_time_val));
  297. } catch (const std::exception& ex) {
  298. isc_throw(BadValue, "Database time value is invalid: "
  299. << db_time_val);
  300. }
  301. }
  302. /// @brief Gets a pointer to the raw column value in a result set row
  303. ///
  304. /// Given a result set, row, and column return a const char* pointer to
  305. /// the data value in the result set. The pointer is valid as long as
  306. /// the result set has not been freed. It may point to text or binary
  307. /// data depending on how query was structured. You should not attempt
  308. /// to free this pointer.
  309. ///
  310. /// @param r the result set containing the query results
  311. /// @param row the row number within the result set
  312. /// @param col the column number within the row
  313. ///
  314. /// @return a const char* pointer to the column's raw data
  315. /// @throw DbOperationError if the value cannot be fetched.
  316. const char* getRawColumnValue(PGresult*& r, const int row,
  317. const size_t col) const {
  318. const char* value = PQgetvalue(r, row, col);
  319. if (!value) {
  320. isc_throw(DbOperationError, "getRawColumnValue no data for :"
  321. << getColumnLabel(col) << " row:" << row);
  322. }
  323. return (value);
  324. }
  325. /// @brief Converts a column in a row in a result set to a boolean.
  326. ///
  327. /// @param r the result set containing the query results
  328. /// @param row the row number within the result set
  329. /// @param col the column number within the row
  330. /// @param[out] value parameter to receive the converted value
  331. ///
  332. /// @throw DbOperationError if the value cannot be fetched or is
  333. /// invalid.
  334. void getColumnValue(PGresult*& r, const int row, const size_t col,
  335. bool &value) const {
  336. const char* data = getRawColumnValue(r, row, col);
  337. if (!strlen(data) || *data == 'f') {
  338. value = false;
  339. } else if (*data == 't') {
  340. value = true;
  341. } else {
  342. isc_throw(DbOperationError, "Invalid boolean data: " << data
  343. << " for: " << getColumnLabel(col) << " row:" << row
  344. << " : must be 't' or 'f'");
  345. }
  346. }
  347. /// @brief Converts a column in a row in a result set to a uint32_t.
  348. ///
  349. /// @param r the result set containing the query results
  350. /// @param row the row number within the result set
  351. /// @param col the column number within the row
  352. /// @param[out] value parameter to receive the converted value
  353. ///
  354. /// @throw DbOperationError if the value cannot be fetched or is
  355. /// invalid.
  356. void getColumnValue(PGresult*& r, const int row, const size_t col,
  357. uint32_t &value) const {
  358. const char* data = getRawColumnValue(r, row, col);
  359. try {
  360. value = boost::lexical_cast<uint32_t>(data);
  361. } catch (const std::exception& ex) {
  362. isc_throw(DbOperationError, "Invalid uint32_t data: " << data
  363. << " for: " << getColumnLabel(col) << " row:" << row
  364. << " : " << ex.what());
  365. }
  366. }
  367. /// @brief Converts a column in a row in a result set to a uint8_t.
  368. ///
  369. /// @param r the result set containing the query results
  370. /// @param row the row number within the result set
  371. /// @param col the column number within the row
  372. /// @param[out] value parameter to receive the converted value
  373. ///
  374. /// @throw DbOperationError if the value cannot be fetched or is
  375. /// invalid.
  376. void getColumnValue(PGresult*& r, const int row, const size_t col,
  377. uint8_t &value) const {
  378. const char* data = getRawColumnValue(r, row, col);
  379. try {
  380. // lexically casting as uint8_t doesn't convert from char
  381. // so we use uint16_t and implicitly convert.
  382. value = boost::lexical_cast<uint16_t>(data);
  383. } catch (const std::exception& ex) {
  384. isc_throw(DbOperationError, "Invalid uint8_t data: " << data
  385. << " for: " << getColumnLabel(col) << " row:" << row
  386. << " : " << ex.what());
  387. }
  388. }
  389. /// @brief Converts a column in a row in a result set to a Lease6::Type
  390. ///
  391. /// @param r the result set containing the query results
  392. /// @param row the row number within the result set
  393. /// @param col the column number within the row
  394. /// @param[out] value parameter to receive the converted value
  395. ///
  396. /// @throw DbOperationError if the value cannot be fetched or is
  397. /// invalid.
  398. void getColumnValue(PGresult*& r, const int row, const size_t col,
  399. Lease6::Type& value) const {
  400. uint32_t raw_value = 0;
  401. getColumnValue(r, row , col, raw_value);
  402. switch (raw_value) {
  403. case Lease6::TYPE_NA:
  404. value = Lease6::TYPE_NA;
  405. break;
  406. case Lease6::TYPE_TA:
  407. value = Lease6::TYPE_TA;
  408. break;
  409. case Lease6::TYPE_PD:
  410. value = Lease6::TYPE_PD;
  411. break;
  412. default:
  413. isc_throw(DbOperationError, "Invalid lease type: " << raw_value
  414. << " for: " << getColumnLabel(col) << " row:" << row);
  415. }
  416. }
  417. /// @brief Converts a column in a row in a result set to a binary bytes
  418. ///
  419. /// Method is used to convert columns stored as BYTEA into a buffer of
  420. /// binary bytes, (uint8_t). It uses PQunescapeBytea to do the conversion.
  421. ///
  422. /// @param r the result set containing the query results
  423. /// @param row the row number within the result set
  424. /// @param col the column number within the row
  425. /// @param[out] buffer pre-allocated buffer to which the converted bytes
  426. /// will be stored.
  427. /// @param buffer_size size of the output buffer
  428. /// @param[out] bytes_converted number of bytes converted
  429. /// value
  430. ///
  431. /// @throw DbOperationError if the value cannot be fetched or is
  432. /// invalid.
  433. void convertFromBytea(PGresult*& r, const int row, const size_t col,
  434. uint8_t* buffer,
  435. const size_t buffer_size,
  436. size_t &bytes_converted) const {
  437. // Returns converted bytes in a dynamically allocated buffer, and
  438. // sets bytes_converted.
  439. unsigned char* bytes = PQunescapeBytea((const unsigned char*)
  440. (getRawColumnValue(r, row, col)),
  441. &bytes_converted);
  442. // Unlikely it couldn't allocate it but you never know.
  443. if (!bytes) {
  444. isc_throw (DbOperationError, "PQunescapeBytea failed for:"
  445. << getColumnLabel(col) << " row:" << row);
  446. }
  447. // Make sure it's not larger than expected.
  448. if (bytes_converted > buffer_size) {
  449. // Free the allocated buffer first!
  450. PQfreemem(bytes);
  451. isc_throw (DbOperationError, "Converted data size: "
  452. << bytes_converted << " is too large for: "
  453. << getColumnLabel(col) << " row:" << row);
  454. }
  455. // Copy from the allocated buffer to caller's buffer the free up
  456. // the allocated buffer.
  457. memcpy(buffer, bytes, bytes_converted);
  458. PQfreemem(bytes);
  459. }
  460. /// @brief Returns column label given a column number
  461. std::string getColumnLabel(const size_t column) const {
  462. if (column > column_labels_.size()) {
  463. ostringstream os;
  464. os << "Unknown column:" << column;
  465. return (os.str());
  466. }
  467. return (column_labels_[column]);
  468. }
  469. protected:
  470. /// @brief Stores text labels for columns, currently only used for
  471. /// logging and errors.
  472. std::vector<std::string>column_labels_;
  473. /// @brief Common Instance members used for binding and conversion
  474. //@{
  475. std::string addr_str_;
  476. uint32_t valid_lifetime_;
  477. std::string valid_lft_str_;
  478. time_t expire_;
  479. std::string expire_str_;
  480. uint32_t subnet_id_;
  481. std::string subnet_id_str_;
  482. time_t cltt_;
  483. bool fqdn_fwd_;
  484. bool fqdn_rev_;
  485. std::string hostname_;
  486. //@}
  487. };
  488. /// @brief Supports exchanging IPv4 leases with PostgreSQL.
  489. class PgSqlLease4Exchange : public PgSqlLeaseExchange {
  490. private:
  491. /// @brief Column numbers for each column in the Lease4 table.
  492. /// These are used for both retrieving data and for looking up
  493. /// column labels for logging. Note that their numeric order
  494. /// MUST match that of the column order in the Lease4 table.
  495. static const size_t ADDRESS_COL = 0;
  496. static const size_t HWADDR_COL = 1;
  497. static const size_t CLIENT_ID_COL = 2;
  498. static const size_t VALID_LIFETIME_COL = 3;
  499. static const size_t EXPIRE_COL = 4;
  500. static const size_t SUBNET_ID_COL = 5;
  501. static const size_t FQDN_FWD_COL = 6;
  502. static const size_t FQDN_REV_COL = 7;
  503. static const size_t HOSTNAME_COL = 8;
  504. /// @brief Number of columns in the table holding DHCPv4 leases.
  505. static const size_t LEASE_COLUMNS = 9;
  506. public:
  507. /// @brief Default constructor
  508. PgSqlLease4Exchange()
  509. : lease_(), addr4_(0), hwaddr_length_(0), hwaddr_(hwaddr_length_),
  510. client_id_length_(0) {
  511. BOOST_STATIC_ASSERT(8 < LEASE_COLUMNS);
  512. memset(hwaddr_buffer_, 0, sizeof(hwaddr_buffer_));
  513. memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
  514. // Set the column names (for error messages)
  515. column_labels_.push_back("address");
  516. column_labels_.push_back("hwaddr");
  517. column_labels_.push_back("client_id");
  518. column_labels_.push_back("valid_lifetime");
  519. column_labels_.push_back("expire");
  520. column_labels_.push_back("subnet_id");
  521. column_labels_.push_back("fqdn_fwd");
  522. column_labels_.push_back("fqdn_rev");
  523. column_labels_.push_back("hostname");
  524. }
  525. /// @brief Creates the bind array for sending Lease4 data to the database.
  526. ///
  527. /// Converts each Lease4 member into the appropriate form and adds it
  528. /// to the bind array. Note that the array additions must occur in the
  529. /// order the columns are specified in the SQL statement. By convention
  530. /// all columns in the table are explicitly listed in the SQL statement(s)
  531. /// in the same order as they occur in the table.
  532. ///
  533. /// @param lease Lease4 object that is to be written to the database
  534. /// @param[out] bind_array array to populate with the lease data values
  535. ///
  536. /// @throw DbOperationError if bind_array cannot be populated.
  537. void createBindForSend(const Lease4Ptr& lease, PsqlBindArray& bind_array) {
  538. if (!lease) {
  539. isc_throw(BadValue, "createBindForSend:: Lease4 object is NULL");
  540. }
  541. // Store lease object to ensure it remains valid.
  542. lease_ = lease;
  543. try {
  544. addr_str_ = boost::lexical_cast<std::string>
  545. (static_cast<uint32_t>(lease->addr_));
  546. bind_array.add(addr_str_);
  547. if (lease->hwaddr_ && !lease->hwaddr_->hwaddr_.empty()) {
  548. // PostgreSql does not provide MAX on variable length types
  549. // so we have to enforce it ourselves.
  550. if (lease->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
  551. isc_throw(DbOperationError, "Hardware address length : "
  552. << lease_->hwaddr_->hwaddr_.size()
  553. << " exceeds maximum allowed of: "
  554. << HWAddr::MAX_HWADDR_LEN);
  555. }
  556. bind_array.add(lease->hwaddr_->hwaddr_);
  557. } else {
  558. bind_array.add("");
  559. }
  560. if (lease->client_id_) {
  561. bind_array.add(lease->client_id_->getClientId());
  562. } else {
  563. bind_array.add("");
  564. }
  565. valid_lft_str_ = boost::lexical_cast<std::string>
  566. (lease->valid_lft_);
  567. bind_array.add(valid_lft_str_);
  568. expire_str_ = convertToDatabaseTime(lease->cltt_, lease_->valid_lft_);
  569. bind_array.add(expire_str_);
  570. subnet_id_str_ = boost::lexical_cast<std::string>
  571. (lease->subnet_id_);
  572. bind_array.add(subnet_id_str_);
  573. bind_array.add(lease->fqdn_fwd_);
  574. bind_array.add(lease->fqdn_rev_);
  575. bind_array.add(lease->hostname_);
  576. } catch (const std::exception& ex) {
  577. isc_throw(DbOperationError,
  578. "Could not create bind array for Lease4: "
  579. << lease_->addr_.toText() << ", reason: " << ex.what());
  580. }
  581. }
  582. /// @brief Creates a Lease4 object from a given row in a result set.
  583. ///
  584. /// @param r result set containing one or rows from the Lease4 table
  585. /// @param row row number within the result set from to create the Lease4
  586. /// object.
  587. ///
  588. /// @return Lease4Ptr to the newly created Lease4 object
  589. /// @throw DbOperationError if the lease cannot be created.
  590. Lease4Ptr convertFromDatabase(PGresult*& r, int row) {
  591. try {
  592. getColumnValue(r, row, ADDRESS_COL, addr4_);
  593. convertFromBytea(r, row, HWADDR_COL, hwaddr_buffer_,
  594. sizeof(hwaddr_buffer_), hwaddr_length_);
  595. convertFromBytea(r, row, CLIENT_ID_COL, client_id_buffer_,
  596. sizeof(client_id_buffer_), client_id_length_);
  597. getColumnValue(r, row, VALID_LIFETIME_COL, valid_lifetime_);
  598. expire_ = convertFromDatabaseTime(getRawColumnValue(r, row,
  599. EXPIRE_COL));
  600. getColumnValue(r, row , SUBNET_ID_COL, subnet_id_);
  601. cltt_ = expire_ - valid_lifetime_;
  602. getColumnValue(r, row, FQDN_FWD_COL, fqdn_fwd_);
  603. getColumnValue(r, row, FQDN_REV_COL, fqdn_rev_);
  604. hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
  605. HWAddrPtr hwaddr(new HWAddr(hwaddr_buffer_, hwaddr_length_,
  606. HTYPE_ETHER));
  607. return (Lease4Ptr(new Lease4(addr4_, hwaddr,
  608. client_id_buffer_, client_id_length_,
  609. valid_lifetime_, 0, 0, cltt_,
  610. subnet_id_, fqdn_fwd_, fqdn_rev_,
  611. hostname_)));
  612. } catch (const std::exception& ex) {
  613. isc_throw(DbOperationError,
  614. "Could not convert data to Lease4, reason: "
  615. << ex.what());
  616. }
  617. }
  618. private:
  619. /// @brief Lease4 object currently being sent to the database.
  620. /// Storing this value ensures that it remains in scope while any bindings
  621. /// that refer to its contents are in use.
  622. Lease4Ptr lease_;
  623. /// @Brief Lease4 specific members used for binding and conversion.
  624. uint32_t addr4_;
  625. size_t hwaddr_length_;
  626. std::vector<uint8_t> hwaddr_;
  627. uint8_t hwaddr_buffer_[HWAddr::MAX_HWADDR_LEN];
  628. size_t client_id_length_;
  629. uint8_t client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
  630. };
  631. /// @brief Supports exchanging IPv6 leases with PostgreSQL.
  632. class PgSqlLease6Exchange : public PgSqlLeaseExchange {
  633. private:
  634. /// @brief Column numbers for each column in the Lease6 table.
  635. /// These are used for both retrieving data and for looking up
  636. /// column labels for logging. Note that their numeric order
  637. /// MUST match that of the column order in the Lease6 table.
  638. //@{
  639. static const int ADDRESS_COL = 0;
  640. static const int DUID_COL = 1;
  641. static const int VALID_LIFETIME_COL = 2;
  642. static const int EXPIRE_COL = 3;
  643. static const int SUBNET_ID_COL = 4;
  644. static const int PREF_LIFETIME_COL = 5;
  645. static const int LEASE_TYPE_COL = 6;
  646. static const int IAID_COL = 7;
  647. static const int PREFIX_LEN_COL = 8;
  648. static const int FQDN_FWD_COL = 9;
  649. static const int FQDN_REV_COL = 10;
  650. static const int HOSTNAME_COL = 11;
  651. //@}
  652. /// @brief Number of columns in the table holding DHCPv4 leases.
  653. static const size_t LEASE_COLUMNS = 12;
  654. public:
  655. PgSqlLease6Exchange()
  656. : lease_(), duid_length_(0), duid_(), iaid_(0), iaid_str_(""),
  657. lease_type_(Lease6::TYPE_NA), lease_type_str_(""), prefix_len_(0),
  658. prefix_len_str_(""), pref_lifetime_(0), preferred_lft_str_("") {
  659. BOOST_STATIC_ASSERT(11 < LEASE_COLUMNS);
  660. memset(duid_buffer_, 0, sizeof(duid_buffer_));
  661. // Set the column names (for error messages)
  662. column_labels_.push_back("address");
  663. column_labels_.push_back("duid");
  664. column_labels_.push_back("valid_lifetime");
  665. column_labels_.push_back("expire");
  666. column_labels_.push_back("subnet_id");
  667. column_labels_.push_back("pref_lifetime");
  668. column_labels_.push_back("lease_type");
  669. column_labels_.push_back("iaid");
  670. column_labels_.push_back("prefix_len");
  671. column_labels_.push_back("fqdn_fwd");
  672. column_labels_.push_back("fqdn_rev");
  673. column_labels_.push_back("hostname");
  674. }
  675. /// @brief Creates the bind array for sending Lease6 data to the database.
  676. ///
  677. /// Converts each Lease6 member into the appropriate form and adds it
  678. /// to the bind array. Note that the array additions must occur in the
  679. /// order the columns are specified in the SQL statement. By convention
  680. /// all columns in the table are explicitly listed in the SQL statement(s)
  681. /// in the same order as they occur in the table.
  682. ///
  683. /// @param lease Lease6 object that is to be written to the database
  684. /// @param[out] bind_array array to populate with the lease data values
  685. ///
  686. /// @throw DbOperationError if bind_array cannot be populated.
  687. void createBindForSend(const Lease6Ptr& lease, PsqlBindArray& bind_array) {
  688. if (!lease) {
  689. isc_throw(BadValue, "createBindForSend:: Lease6 object is NULL");
  690. }
  691. // Store lease object to ensure it remains valid.
  692. lease_ = lease;
  693. try {
  694. addr_str_ = lease_->addr_.toText();
  695. bind_array.add(addr_str_);
  696. if (lease_->duid_) {
  697. bind_array.add(lease_->duid_->getDuid());
  698. } else {
  699. isc_throw (BadValue, "IPv6 Lease cannot have a null DUID");
  700. }
  701. valid_lft_str_ = boost::lexical_cast<std::string>
  702. (lease->valid_lft_);
  703. bind_array.add(valid_lft_str_);
  704. expire_str_ = convertToDatabaseTime(lease->cltt_, lease_->valid_lft_);
  705. bind_array.add(expire_str_);
  706. subnet_id_str_ = boost::lexical_cast<std::string>
  707. (lease->subnet_id_);
  708. bind_array.add(subnet_id_str_);
  709. preferred_lft_str_ = boost::lexical_cast<std::string>
  710. (lease_->preferred_lft_);
  711. bind_array.add(preferred_lft_str_);
  712. lease_type_str_ = boost::lexical_cast<std::string>(lease_->type_);
  713. bind_array.add(lease_type_str_);
  714. iaid_str_ = boost::lexical_cast<std::string>(lease_->iaid_);
  715. bind_array.add(iaid_str_);
  716. prefix_len_str_ = boost::lexical_cast<std::string>
  717. (static_cast<unsigned int>(lease_->prefixlen_));
  718. bind_array.add(prefix_len_str_);
  719. bind_array.add(lease->fqdn_fwd_);
  720. bind_array.add(lease->fqdn_rev_);
  721. bind_array.add(lease->hostname_);
  722. } catch (const std::exception& ex) {
  723. isc_throw(DbOperationError,
  724. "Could not create bind array from Lease6: "
  725. << lease_->addr_.toText() << ", reason: " << ex.what());
  726. }
  727. }
  728. /// @brief Creates a Lease6 object from a given row in a result set.
  729. ///
  730. /// @param r result set containing one or rows from the Lease6 table
  731. /// @param row row number within the result set from to create the Lease6
  732. /// object.
  733. ///
  734. /// @return Lease6Ptr to the newly created Lease4 object
  735. /// @throw DbOperationError if the lease cannot be created.
  736. Lease6Ptr convertFromDatabase(PGresult*& r, int row) {
  737. try {
  738. isc::asiolink::IOAddress addr(getIPv6Value(r, row, ADDRESS_COL));
  739. convertFromBytea(r, row, DUID_COL, duid_buffer_,
  740. sizeof(duid_buffer_), duid_length_);
  741. DuidPtr duid_ptr(new DUID(duid_buffer_, duid_length_));
  742. getColumnValue(r, row, VALID_LIFETIME_COL, valid_lifetime_);
  743. expire_ = convertFromDatabaseTime(getRawColumnValue(r, row,
  744. EXPIRE_COL));
  745. cltt_ = expire_ - valid_lifetime_;
  746. getColumnValue(r, row , SUBNET_ID_COL, subnet_id_);
  747. getColumnValue(r, row , PREF_LIFETIME_COL, pref_lifetime_);
  748. getColumnValue(r, row, LEASE_TYPE_COL, lease_type_);
  749. getColumnValue(r, row , IAID_COL, iaid_);
  750. getColumnValue(r, row , PREFIX_LEN_COL, prefix_len_);
  751. getColumnValue(r, row, FQDN_FWD_COL, fqdn_fwd_);
  752. getColumnValue(r, row, FQDN_REV_COL, fqdn_rev_);
  753. hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
  754. /// @todo: implement this in #3557.
  755. HWAddrPtr hwaddr;
  756. Lease6Ptr result(new Lease6(lease_type_, addr, duid_ptr, iaid_,
  757. pref_lifetime_, valid_lifetime_, 0, 0,
  758. subnet_id_, fqdn_fwd_, fqdn_rev_,
  759. hostname_, hwaddr, prefix_len_));
  760. result->cltt_ = cltt_;
  761. return (result);
  762. } catch (const std::exception& ex) {
  763. isc_throw(DbOperationError,
  764. "Could not convert data to Lease6, reason: "
  765. << ex.what());
  766. }
  767. }
  768. /// @brief Converts a column in a row in a result set into IPv6 address.
  769. ///
  770. /// @param r the result set containing the query results
  771. /// @param row the row number within the result set
  772. /// @param col the column number within the row
  773. ///
  774. /// @return isc::asiolink::IOAddress containing the IPv6 address.
  775. /// @throw DbOperationError if the value cannot be fetched or is
  776. /// invalid.
  777. isc::asiolink::IOAddress getIPv6Value(PGresult*& r, const int row,
  778. const size_t col) const {
  779. const char* data = getRawColumnValue(r, row, col);
  780. try {
  781. return (isc::asiolink::IOAddress(data));
  782. } catch (const std::exception& ex) {
  783. isc_throw(DbOperationError, "Cannot convert data: " << data
  784. << " for: " << getColumnLabel(col) << " row:" << row
  785. << " : " << ex.what());
  786. }
  787. }
  788. private:
  789. /// @brief Lease6 object currently being sent to the database.
  790. /// Storing this value ensures that it remains in scope while any bindings
  791. /// that refer to its contents are in use.
  792. Lease6Ptr lease_;
  793. /// @brief Lease6 specific members for binding and conversion.
  794. //@{
  795. size_t duid_length_;
  796. vector<uint8_t> duid_;
  797. uint8_t duid_buffer_[DUID::MAX_DUID_LEN];
  798. uint32_t iaid_;
  799. std::string iaid_str_;
  800. Lease6::Type lease_type_;
  801. std::string lease_type_str_;
  802. uint8_t prefix_len_;
  803. std::string prefix_len_str_;
  804. uint32_t pref_lifetime_;
  805. std::string preferred_lft_str_;
  806. //@}
  807. };
  808. PgSqlLeaseMgr::PgSqlLeaseMgr(const LeaseMgr::ParameterMap& parameters)
  809. : LeaseMgr(parameters), exchange4_(new PgSqlLease4Exchange()),
  810. exchange6_(new PgSqlLease6Exchange()), conn_(NULL) {
  811. openDatabase();
  812. prepareStatements();
  813. }
  814. PgSqlLeaseMgr::~PgSqlLeaseMgr() {
  815. if (conn_) {
  816. // Deallocate the prepared queries.
  817. PGresult* r = PQexec(conn_, "DEALLOCATE all");
  818. if(PQresultStatus(r) != PGRES_COMMAND_OK) {
  819. // Highly unlikely but we'll log it and go on.
  820. LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_DEALLOC_ERROR)
  821. .arg(PQerrorMessage(conn_));
  822. }
  823. PQclear(r);
  824. PQfinish(conn_);
  825. conn_ = NULL;
  826. }
  827. }
  828. void
  829. PgSqlLeaseMgr::prepareStatements() {
  830. for(int i = 0; tagged_statements[i].text != NULL; ++ i) {
  831. // Prepare all statements queries with all known fields datatype
  832. PGresult* r = PQprepare(conn_, tagged_statements[i].name,
  833. tagged_statements[i].text,
  834. tagged_statements[i].nbparams,
  835. tagged_statements[i].types);
  836. if(PQresultStatus(r) != PGRES_COMMAND_OK) {
  837. PQclear(r);
  838. isc_throw(DbOperationError,
  839. "unable to prepare PostgreSQL statement: "
  840. << tagged_statements[i].text << ", reason: "
  841. << PQerrorMessage(conn_));
  842. }
  843. PQclear(r);
  844. }
  845. }
  846. void
  847. PgSqlLeaseMgr::openDatabase() {
  848. string dbconnparameters;
  849. string shost = "localhost";
  850. try {
  851. shost = getParameter("host");
  852. } catch(...) {
  853. // No host. Fine, we'll use "localhost"
  854. }
  855. dbconnparameters += "host = '" + shost + "'" ;
  856. string suser;
  857. try {
  858. suser = getParameter("user");
  859. dbconnparameters += " user = '" + suser + "'";
  860. } catch(...) {
  861. // No user. Fine, we'll use NULL
  862. }
  863. string spassword;
  864. try {
  865. spassword = getParameter("password");
  866. dbconnparameters += " password = '" + spassword + "'";
  867. } catch(...) {
  868. // No password. Fine, we'll use NULL
  869. }
  870. string sname;
  871. try {
  872. sname= getParameter("name");
  873. dbconnparameters += " dbname = '" + sname + "'";
  874. } catch(...) {
  875. // No database name. Throw a "NoDatabaseName" exception
  876. isc_throw(NoDatabaseName, "must specify a name for the database");
  877. }
  878. conn_ = PQconnectdb(dbconnparameters.c_str());
  879. if (conn_ == NULL) {
  880. isc_throw(DbOpenError, "could not allocate connection object");
  881. }
  882. if (PQstatus(conn_) != CONNECTION_OK) {
  883. // If we have a connection object, we have to call finish
  884. // to release it, but grab the error message first.
  885. std::string error_message = PQerrorMessage(conn_);
  886. PQfinish(conn_);
  887. conn_ = NULL;
  888. isc_throw(DbOpenError, error_message);
  889. }
  890. }
  891. bool
  892. PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
  893. PsqlBindArray& bind_array) {
  894. PGresult* r = PQexecPrepared(conn_, tagged_statements[stindex].name,
  895. tagged_statements[stindex].nbparams,
  896. &bind_array.values_[0],
  897. &bind_array.lengths_[0],
  898. &bind_array.formats_[0], 0);
  899. int s = PQresultStatus(r);
  900. if (s != PGRES_COMMAND_OK) {
  901. // Failure: check for the special case of duplicate entry. If this is
  902. // the case, we return false to indicate that the row was not added.
  903. // Otherwise we throw an exception.
  904. if (compareError(r, DUPLICATE_KEY)) {
  905. PQclear(r);
  906. return (false);
  907. }
  908. const char* errorMsg = PQerrorMessage(conn_);
  909. PQclear(r);
  910. isc_throw(DbOperationError, "unable to INSERT for " <<
  911. tagged_statements[stindex].name << ", reason: " <<
  912. errorMsg);
  913. }
  914. PQclear(r);
  915. return (true);
  916. }
  917. bool PgSqlLeaseMgr::compareError(PGresult*& r, const char* error_state) {
  918. const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
  919. // PostgreSQL garuantees it will always be 5 characters long
  920. return ((sqlstate != NULL) &&
  921. (memcmp(sqlstate, error_state, STATECODE_LEN) == 0));
  922. }
  923. bool
  924. PgSqlLeaseMgr::addLease(const Lease4Ptr& lease) {
  925. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  926. DHCPSRV_PGSQL_ADD_ADDR4).arg(lease->addr_.toText());
  927. PsqlBindArray bind_array;
  928. exchange4_->createBindForSend(lease, bind_array);
  929. return (addLeaseCommon(INSERT_LEASE4, bind_array));
  930. }
  931. bool
  932. PgSqlLeaseMgr::addLease(const Lease6Ptr& lease) {
  933. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  934. DHCPSRV_PGSQL_ADD_ADDR6).arg(lease->addr_.toText());
  935. PsqlBindArray bind_array;
  936. exchange6_->createBindForSend(lease, bind_array);
  937. return (addLeaseCommon(INSERT_LEASE6, bind_array));
  938. }
  939. template <typename Exchange, typename LeaseCollection>
  940. void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
  941. PsqlBindArray& bind_array,
  942. Exchange& exchange,
  943. LeaseCollection& result,
  944. bool single) const {
  945. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  946. DHCPSRV_PGSQL_GET_ADDR4).arg(tagged_statements[stindex].name);
  947. PGresult* r = PQexecPrepared(conn_, tagged_statements[stindex].name,
  948. tagged_statements[stindex].nbparams,
  949. &bind_array.values_[0],
  950. &bind_array.lengths_[0],
  951. &bind_array.formats_[0], 0);
  952. checkStatementError(r, stindex);
  953. int rows = PQntuples(r);
  954. if (single && rows > 1) {
  955. PQclear(r);
  956. isc_throw(MultipleRecords, "multiple records were found in the "
  957. "database where only one was expected for query "
  958. << tagged_statements[stindex].name);
  959. }
  960. for(int i = 0; i < rows; ++ i) {
  961. result.push_back(exchange->convertFromDatabase(r, i));
  962. }
  963. PQclear(r);
  964. }
  965. void
  966. PgSqlLeaseMgr::getLease(StatementIndex stindex, PsqlBindArray& bind_array,
  967. Lease4Ptr& result) const {
  968. // Create appropriate collection object and get all leases matching
  969. // the selection criteria. The "single" parameter is true to indicate
  970. // that the called method should throw an exception if multiple
  971. // matching records are found: this particular method is called when only
  972. // one or zero matches is expected.
  973. Lease4Collection collection;
  974. getLeaseCollection(stindex, bind_array, exchange4_, collection, true);
  975. // Return single record if present, else clear the lease.
  976. if (collection.empty()) {
  977. result.reset();
  978. } else {
  979. result = *collection.begin();
  980. }
  981. }
  982. void
  983. PgSqlLeaseMgr::getLease(StatementIndex stindex, PsqlBindArray& bind_array,
  984. Lease6Ptr& result) const {
  985. // Create appropriate collection object and get all leases matching
  986. // the selection criteria. The "single" parameter is true to indicate
  987. // that the called method should throw an exception if multiple
  988. // matching records are found: this particular method is called when only
  989. // one or zero matches is expected.
  990. Lease6Collection collection;
  991. getLeaseCollection(stindex, bind_array, exchange6_, collection, true);
  992. // Return single record if present, else clear the lease.
  993. if (collection.empty()) {
  994. result.reset();
  995. } else {
  996. result = *collection.begin();
  997. }
  998. }
  999. Lease4Ptr
  1000. PgSqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
  1001. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1002. DHCPSRV_PGSQL_GET_ADDR4).arg(addr.toText());
  1003. // Set up the WHERE clause value
  1004. PsqlBindArray bind_array;
  1005. // LEASE ADDRESS
  1006. std::string addr_str = boost::lexical_cast<std::string>
  1007. (static_cast<uint32_t>(addr));
  1008. bind_array.add(addr_str);
  1009. // Get the data
  1010. Lease4Ptr result;
  1011. getLease(GET_LEASE4_ADDR, bind_array, result);
  1012. return (result);
  1013. }
  1014. Lease4Collection
  1015. PgSqlLeaseMgr::getLease4(const HWAddr& hwaddr) const {
  1016. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1017. DHCPSRV_PGSQL_GET_HWADDR).arg(hwaddr.toText());
  1018. // Set up the WHERE clause value
  1019. PsqlBindArray bind_array;
  1020. // HWADDR
  1021. if (!hwaddr.hwaddr_.empty()) {
  1022. bind_array.add(hwaddr.hwaddr_);
  1023. } else {
  1024. bind_array.add("");
  1025. }
  1026. // Get the data
  1027. Lease4Collection result;
  1028. getLeaseCollection(GET_LEASE4_HWADDR, bind_array, result);
  1029. return (result);
  1030. }
  1031. Lease4Ptr
  1032. PgSqlLeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
  1033. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1034. DHCPSRV_PGSQL_GET_SUBID_HWADDR)
  1035. .arg(subnet_id).arg(hwaddr.toText());
  1036. // Set up the WHERE clause value
  1037. PsqlBindArray bind_array;
  1038. // HWADDR
  1039. if (!hwaddr.hwaddr_.empty()) {
  1040. bind_array.add(hwaddr.hwaddr_);
  1041. } else {
  1042. bind_array.add("");
  1043. }
  1044. // SUBNET_ID
  1045. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  1046. bind_array.add(subnet_id_str);
  1047. // Get the data
  1048. Lease4Ptr result;
  1049. getLease(GET_LEASE4_HWADDR_SUBID, bind_array, result);
  1050. return (result);
  1051. }
  1052. Lease4Collection
  1053. PgSqlLeaseMgr::getLease4(const ClientId& clientid) const {
  1054. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1055. DHCPSRV_PGSQL_GET_CLIENTID).arg(clientid.toText());
  1056. // Set up the WHERE clause value
  1057. PsqlBindArray bind_array;
  1058. // CLIENT_ID
  1059. bind_array.add(clientid.getClientId());
  1060. // Get the data
  1061. Lease4Collection result;
  1062. getLeaseCollection(GET_LEASE4_CLIENTID, bind_array, result);
  1063. return (result);
  1064. }
  1065. Lease4Ptr
  1066. PgSqlLeaseMgr::getLease4(const ClientId& clientid, SubnetID subnet_id) const {
  1067. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1068. DHCPSRV_PGSQL_GET_SUBID_CLIENTID)
  1069. .arg(subnet_id).arg(clientid.toText());
  1070. // Set up the WHERE clause value
  1071. PsqlBindArray bind_array;
  1072. // CLIENT_ID
  1073. bind_array.add(clientid.getClientId());
  1074. // SUBNET_ID
  1075. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  1076. bind_array.add(subnet_id_str);
  1077. // Get the data
  1078. Lease4Ptr result;
  1079. getLease(GET_LEASE4_CLIENTID_SUBID, bind_array, result);
  1080. return (result);
  1081. }
  1082. Lease4Ptr
  1083. PgSqlLeaseMgr::getLease4(const ClientId&, const HWAddr&, SubnetID) const {
  1084. /// This function is currently not implemented because allocation engine
  1085. /// searches for the lease using HW address or client identifier.
  1086. /// It never uses both parameters in the same time. We need to
  1087. /// consider if this function is needed at all.
  1088. isc_throw(NotImplemented, "The PgSqlLeaseMgr::getLease4 function was"
  1089. " called, but it is not implemented");
  1090. }
  1091. Lease6Ptr
  1092. PgSqlLeaseMgr::getLease6(Lease::Type lease_type,
  1093. const isc::asiolink::IOAddress& addr) const {
  1094. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_GET_ADDR6)
  1095. .arg(addr.toText()).arg(lease_type);
  1096. // Set up the WHERE clause value
  1097. PsqlBindArray bind_array;
  1098. // LEASE ADDRESS
  1099. std::string addr_str = addr.toText();
  1100. bind_array.add(addr_str);
  1101. // LEASE_TYPE
  1102. std::string type_str_ = boost::lexical_cast<std::string>(lease_type);
  1103. bind_array.add(type_str_);
  1104. // ... and get the data
  1105. Lease6Ptr result;
  1106. getLease(GET_LEASE6_ADDR, bind_array, result);
  1107. return (result);
  1108. }
  1109. Lease6Collection
  1110. PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid,
  1111. uint32_t iaid) const {
  1112. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1113. DHCPSRV_PGSQL_GET_IAID_DUID)
  1114. .arg(iaid).arg(duid.toText()).arg(lease_type);
  1115. // Set up the WHERE clause value
  1116. PsqlBindArray bind_array;
  1117. // DUID
  1118. bind_array.add(duid.getDuid());
  1119. // IAID
  1120. std::string iaid_str = boost::lexical_cast<std::string>(iaid);
  1121. bind_array.add(iaid_str);
  1122. // LEASE_TYPE
  1123. std::string lease_type_str = boost::lexical_cast<std::string>(lease_type);
  1124. bind_array.add(lease_type_str);
  1125. // ... and get the data
  1126. Lease6Collection result;
  1127. getLeaseCollection(GET_LEASE6_DUID_IAID, bind_array, result);
  1128. return (result);
  1129. }
  1130. Lease6Collection
  1131. PgSqlLeaseMgr::getLeases6(Lease::Type lease_type, const DUID& duid,
  1132. uint32_t iaid, SubnetID subnet_id) const {
  1133. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1134. DHCPSRV_PGSQL_GET_IAID_SUBID_DUID)
  1135. .arg(iaid).arg(subnet_id).arg(duid.toText()).arg(lease_type);
  1136. // Set up the WHERE clause value
  1137. PsqlBindArray bind_array;
  1138. // LEASE_TYPE
  1139. std::string lease_type_str = boost::lexical_cast<std::string>(lease_type);
  1140. bind_array.add(lease_type_str);
  1141. // DUID
  1142. bind_array.add(duid.getDuid());
  1143. // IAID
  1144. std::string iaid_str = boost::lexical_cast<std::string>(iaid);
  1145. bind_array.add(iaid_str);
  1146. // SUBNET ID
  1147. std::string subnet_id_str = boost::lexical_cast<std::string>(subnet_id);
  1148. bind_array.add(subnet_id_str);
  1149. // ... and get the data
  1150. Lease6Collection result;
  1151. getLeaseCollection(GET_LEASE6_DUID_IAID_SUBID, bind_array, result);
  1152. return (result);
  1153. }
  1154. template <typename LeasePtr>
  1155. void
  1156. PgSqlLeaseMgr::updateLeaseCommon(StatementIndex stindex,
  1157. PsqlBindArray& bind_array,
  1158. const LeasePtr& lease) {
  1159. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1160. DHCPSRV_PGSQL_ADD_ADDR4).arg(tagged_statements[stindex].name);
  1161. PGresult* r = PQexecPrepared(conn_, tagged_statements[stindex].name,
  1162. tagged_statements[stindex].nbparams,
  1163. &bind_array.values_[0],
  1164. &bind_array.lengths_[0],
  1165. &bind_array.formats_[0], 0);
  1166. checkStatementError(r, stindex);
  1167. int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
  1168. PQclear(r);
  1169. // Check success case first as it is the most likely outcome.
  1170. if (affected_rows == 1) {
  1171. return;
  1172. }
  1173. // If no rows affected, lease doesn't exist.
  1174. if (affected_rows == 0) {
  1175. isc_throw(NoSuchLease, "unable to update lease for address " <<
  1176. lease->addr_.toText() << " as it does not exist");
  1177. }
  1178. // Should not happen - primary key constraint should only have selected
  1179. // one row.
  1180. isc_throw(DbOperationError, "apparently updated more than one lease "
  1181. "that had the address " << lease->addr_.toText());
  1182. }
  1183. void
  1184. PgSqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
  1185. const StatementIndex stindex = UPDATE_LEASE4;
  1186. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1187. DHCPSRV_PGSQL_UPDATE_ADDR4).arg(lease->addr_.toText());
  1188. // Create the BIND array for the data being updated
  1189. PsqlBindArray bind_array;
  1190. exchange4_->createBindForSend(lease, bind_array);
  1191. // Set up the WHERE clause and append it to the SQL_BIND array
  1192. std::string addr4_ = boost::lexical_cast<std::string>
  1193. (static_cast<uint32_t>(lease->addr_));
  1194. bind_array.add(addr4_);
  1195. // Drop to common update code
  1196. updateLeaseCommon(stindex, bind_array, lease);
  1197. }
  1198. void
  1199. PgSqlLeaseMgr::updateLease6(const Lease6Ptr& lease) {
  1200. const StatementIndex stindex = UPDATE_LEASE6;
  1201. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1202. DHCPSRV_PGSQL_UPDATE_ADDR6).arg(lease->addr_.toText());
  1203. // Create the BIND array for the data being updated
  1204. PsqlBindArray bind_array;
  1205. exchange6_->createBindForSend(lease, bind_array);
  1206. // Set up the WHERE clause and append it to the BIND array
  1207. std::string addr_str = lease->addr_.toText();
  1208. bind_array.add(addr_str);
  1209. // Drop to common update code
  1210. updateLeaseCommon(stindex, bind_array, lease);
  1211. }
  1212. bool
  1213. PgSqlLeaseMgr::deleteLeaseCommon(StatementIndex stindex,
  1214. PsqlBindArray& bind_array) {
  1215. PGresult* r = PQexecPrepared(conn_, tagged_statements[stindex].name,
  1216. tagged_statements[stindex].nbparams,
  1217. &bind_array.values_[0],
  1218. &bind_array.lengths_[0],
  1219. &bind_array.formats_[0], 0);
  1220. checkStatementError(r, stindex);
  1221. int affected_rows = boost::lexical_cast<int>(PQcmdTuples(r));
  1222. PQclear(r);
  1223. return (affected_rows > 0);
  1224. }
  1225. bool
  1226. PgSqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
  1227. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1228. DHCPSRV_PGSQL_DELETE_ADDR).arg(addr.toText());
  1229. // Set up the WHERE clause value
  1230. PsqlBindArray bind_array;
  1231. if (addr.isV4()) {
  1232. std::string addr4_str = boost::lexical_cast<std::string>
  1233. (static_cast<uint32_t>(addr));
  1234. bind_array.add(addr4_str);
  1235. return (deleteLeaseCommon(DELETE_LEASE4, bind_array));
  1236. }
  1237. std::string addr6_str = addr.toText();
  1238. bind_array.add(addr6_str);
  1239. return (deleteLeaseCommon(DELETE_LEASE6, bind_array));
  1240. }
  1241. string
  1242. PgSqlLeaseMgr::getName() const {
  1243. string name = "";
  1244. try {
  1245. name = getParameter("name");
  1246. } catch (...) {
  1247. // Return an empty name
  1248. }
  1249. return (name);
  1250. }
  1251. void
  1252. PgSqlLeaseMgr::checkStatementError(PGresult*& r, StatementIndex index) const {
  1253. int s = PQresultStatus(r);
  1254. if (s != PGRES_COMMAND_OK && s != PGRES_TUPLES_OK) {
  1255. const char* error_message = PQerrorMessage(conn_);
  1256. PQclear(r);
  1257. isc_throw(DbOperationError, "Statement exec faild:" << " for: "
  1258. << tagged_statements[index].name << ", reason: "
  1259. << error_message);
  1260. }
  1261. }
  1262. string
  1263. PgSqlLeaseMgr::getDescription() const {
  1264. return (string("PostgreSQL Database"));
  1265. }
  1266. pair<uint32_t, uint32_t>
  1267. PgSqlLeaseMgr::getVersion() const {
  1268. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  1269. DHCPSRV_PGSQL_GET_VERSION);
  1270. PGresult* r = PQexecPrepared(conn_, "get_version", 0, NULL, NULL, NULL, 0);
  1271. checkStatementError(r, GET_VERSION);
  1272. istringstream tmp;
  1273. uint32_t version;
  1274. tmp.str(PQgetvalue(r, 0, 0));
  1275. tmp >> version;
  1276. tmp.str("");
  1277. tmp.clear();
  1278. uint32_t minor;
  1279. tmp.str(PQgetvalue(r, 0, 1));
  1280. tmp >> minor;
  1281. PQclear(r);
  1282. return make_pair<uint32_t, uint32_t>(version, minor);
  1283. }
  1284. void
  1285. PgSqlLeaseMgr::commit() {
  1286. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_COMMIT);
  1287. PGresult* r = PQexec(conn_, "COMMIT");
  1288. if (PQresultStatus(r) != PGRES_COMMAND_OK) {
  1289. const char* error_message = PQerrorMessage(conn_);
  1290. PQclear(r);
  1291. isc_throw(DbOperationError, "commit failed: " << error_message);
  1292. }
  1293. PQclear(r);
  1294. }
  1295. void
  1296. PgSqlLeaseMgr::rollback() {
  1297. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_PGSQL_ROLLBACK);
  1298. PGresult* r = PQexec(conn_, "ROLLBACK");
  1299. if (PQresultStatus(r) != PGRES_COMMAND_OK) {
  1300. const char* error_message = PQerrorMessage(conn_);
  1301. PQclear(r);
  1302. isc_throw(DbOperationError, "rollback failed: " << error_message);
  1303. }
  1304. PQclear(r);
  1305. }
  1306. }; // end of isc::dhcp namespace
  1307. }; // end of isc namespace