pgsql_exchange.cc 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright (C) 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 <dhcpsrv/pgsql_exchange.h>
  7. #include <boost/lexical_cast.hpp>
  8. #include <iomanip>
  9. #include <sstream>
  10. #include <vector>
  11. namespace isc {
  12. namespace dhcp {
  13. const int PsqlBindArray::TEXT_FMT = 0;
  14. const int PsqlBindArray::BINARY_FMT = 1;
  15. const char* PsqlBindArray::TRUE_STR = "TRUE";
  16. const char* PsqlBindArray::FALSE_STR = "FALSE";
  17. void PsqlBindArray::add(const char* value) {
  18. values_.push_back(value);
  19. lengths_.push_back(strlen(value));
  20. formats_.push_back(TEXT_FMT);
  21. }
  22. void PsqlBindArray::add(const std::string& value) {
  23. values_.push_back(value.c_str());
  24. lengths_.push_back(value.size());
  25. formats_.push_back(TEXT_FMT);
  26. }
  27. void PsqlBindArray::add(const std::vector<uint8_t>& data) {
  28. values_.push_back(reinterpret_cast<const char*>(&(data[0])));
  29. lengths_.push_back(data.size());
  30. formats_.push_back(BINARY_FMT);
  31. }
  32. void PsqlBindArray::add(const uint8_t* data, const size_t len) {
  33. values_.push_back(reinterpret_cast<const char*>(&(data[0])));
  34. lengths_.push_back(len);
  35. formats_.push_back(BINARY_FMT);
  36. }
  37. void PsqlBindArray::add(const bool& value) {
  38. add(value ? TRUE_STR : FALSE_STR);
  39. }
  40. void PsqlBindArray::add(const uint8_t& byte) {
  41. // We static_cast to an unsigned int, otherwise lexcial_cast may to
  42. // treat byte as a character, which yields "" for unprintable values
  43. bindString(boost::lexical_cast<std::string>
  44. (static_cast<unsigned int>(byte)));
  45. }
  46. void PsqlBindArray::add(const isc::asiolink::IOAddress& addr) {
  47. if (addr.isV4()) {
  48. bindString(boost::lexical_cast<std::string>
  49. (static_cast<uint32_t>(addr)));
  50. } else {
  51. bindString(addr.toText());
  52. }
  53. }
  54. // eventually this should replace add(std::string)
  55. void PsqlBindArray::bindString(const std::string& str) {
  56. bound_strs_.push_back(StringPtr(new std::string(str)));
  57. PsqlBindArray::add((bound_strs_.back())->c_str());
  58. }
  59. std::string PsqlBindArray::toText() const {
  60. std::ostringstream stream;
  61. for (int i = 0; i < values_.size(); ++i) {
  62. stream << i << " : ";
  63. if (formats_[i] == TEXT_FMT) {
  64. stream << "\"" << values_[i] << "\"" << std::endl;
  65. } else {
  66. const char *data = values_[i];
  67. if (lengths_[i] == 0) {
  68. stream << "empty" << std::endl;
  69. } else {
  70. stream << "0x";
  71. for (int i = 0; i < lengths_[i]; ++i) {
  72. stream << std::setfill('0') << std::setw(2)
  73. << std::setbase(16)
  74. << static_cast<unsigned int>(data[i]);
  75. }
  76. stream << std::endl;
  77. }
  78. }
  79. }
  80. return (stream.str());
  81. }
  82. std::string
  83. PgSqlExchange::convertToDatabaseTime(const time_t input_time) {
  84. struct tm tinfo;
  85. char buffer[20];
  86. localtime_r(&input_time, &tinfo);
  87. strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tinfo);
  88. return (std::string(buffer));
  89. }
  90. std::string
  91. PgSqlExchange::convertToDatabaseTime(const time_t cltt,
  92. const uint32_t valid_lifetime) {
  93. // Calculate expiry time. Store it in the 64-bit value so as we can
  94. // detect overflows.
  95. int64_t expire_time_64 = static_cast<int64_t>(cltt)
  96. + static_cast<int64_t>(valid_lifetime);
  97. // It has been observed that the PostgreSQL doesn't deal well with the
  98. // timestamp values beyond the DataSource::MAX_DB_TIME seconds since the
  99. // beginning of the epoch (around year 2038). The value is often
  100. // stored in the database but it is invalid when read back (overflow?).
  101. // Hence, the maximum timestamp value is restricted here.
  102. if (expire_time_64 > DatabaseConnection::MAX_DB_TIME) {
  103. isc_throw(isc::BadValue, "Time value is too large: " << expire_time_64);
  104. }
  105. return (convertToDatabaseTime(static_cast<time_t>(expire_time_64)));
  106. }
  107. time_t
  108. PgSqlExchange::convertFromDatabaseTime(const std::string& db_time_val) {
  109. // Convert string time value to time_t
  110. time_t new_time;
  111. try {
  112. new_time = (boost::lexical_cast<time_t>(db_time_val));
  113. } catch (const std::exception& ex) {
  114. isc_throw(BadValue, "Database time value is invalid: " << db_time_val);
  115. }
  116. return (new_time);
  117. }
  118. const char*
  119. PgSqlExchange::getRawColumnValue(const PgSqlResult& r, const int row,
  120. const size_t col) const {
  121. const char* value = PQgetvalue(r, row, col);
  122. if (!value) {
  123. isc_throw(DbOperationError, "getRawColumnValue no data for :"
  124. << getColumnLabel(col) << " row:" << row);
  125. }
  126. return (value);
  127. }
  128. void
  129. PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
  130. const size_t col, bool &value) const {
  131. const char* data = getRawColumnValue(r, row, col);
  132. if (!strlen(data) || *data == 'f') {
  133. value = false;
  134. } else if (*data == 't') {
  135. value = true;
  136. } else {
  137. isc_throw(DbOperationError, "Invalid boolean data: " << data
  138. << " for: " << getColumnLabel(col) << " row:" << row
  139. << " : must be 't' or 'f'");
  140. }
  141. }
  142. void
  143. PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
  144. const size_t col, uint8_t &value) const {
  145. const char* data = getRawColumnValue(r, row, col);
  146. try {
  147. // lexically casting as uint8_t doesn't convert from char
  148. // so we use uint16_t and implicitly convert.
  149. value = boost::lexical_cast<uint16_t>(data);
  150. } catch (const std::exception& ex) {
  151. isc_throw(DbOperationError, "Invalid uint8_t data: " << data
  152. << " for: " << getColumnLabel(col) << " row:" << row
  153. << " : " << ex.what());
  154. }
  155. }
  156. void
  157. PgSqlExchange::convertFromBytea(const PgSqlResult& r, const int row,
  158. const size_t col, uint8_t* buffer,
  159. const size_t buffer_size,
  160. size_t &bytes_converted) const {
  161. // Returns converted bytes in a dynamically allocated buffer, and
  162. // sets bytes_converted.
  163. unsigned char* bytes = PQunescapeBytea((const unsigned char*)
  164. (getRawColumnValue(r, row, col)),
  165. &bytes_converted);
  166. // Unlikely it couldn't allocate it but you never know.
  167. if (!bytes) {
  168. isc_throw (DbOperationError, "PQunescapeBytea failed for:"
  169. << getColumnLabel(col) << " row:" << row);
  170. }
  171. // Make sure it's not larger than expected.
  172. if (bytes_converted > buffer_size) {
  173. // Free the allocated buffer first!
  174. PQfreemem(bytes);
  175. isc_throw (DbOperationError, "Converted data size: "
  176. << bytes_converted << " is too large for: "
  177. << getColumnLabel(col) << " row:" << row);
  178. }
  179. // Copy from the allocated buffer to caller's buffer the free up
  180. // the allocated buffer.
  181. memcpy(buffer, bytes, bytes_converted);
  182. PQfreemem(bytes);
  183. }
  184. std::string
  185. PgSqlExchange::getColumnLabel(const size_t column) const {
  186. if (column > column_labels_.size()) {
  187. std::ostringstream os;
  188. os << "Unknown column:" << column;
  189. return (os.str());
  190. }
  191. return (column_labels_[column]);
  192. }
  193. }; // end of isc::dhcp namespace
  194. }; // end of isc namespace