pgsql_lease_mgr.cc 62 KB

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