pgsql_lease_mgr.cc 64 KB

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