123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- #include <dhcpsrv/pgsql_exchange.h>
- #include <boost/lexical_cast.hpp>
- #include <iomanip>
- #include <sstream>
- #include <vector>
- namespace isc {
- namespace dhcp {
- const int PsqlBindArray::TEXT_FMT = 0;
- const int PsqlBindArray::BINARY_FMT = 1;
- const char* PsqlBindArray::TRUE_STR = "TRUE";
- const char* PsqlBindArray::FALSE_STR = "FALSE";
- void PsqlBindArray::add(const char* value) {
- values_.push_back(value);
- lengths_.push_back(strlen(value));
- formats_.push_back(TEXT_FMT);
- }
- void PsqlBindArray::add(const std::string& value) {
- values_.push_back(value.c_str());
- lengths_.push_back(value.size());
- formats_.push_back(TEXT_FMT);
- }
- void PsqlBindArray::add(const std::vector<uint8_t>& data) {
- values_.push_back(reinterpret_cast<const char*>(&(data[0])));
- lengths_.push_back(data.size());
- formats_.push_back(BINARY_FMT);
- }
- void PsqlBindArray::add(const uint8_t* data, const size_t len) {
- values_.push_back(reinterpret_cast<const char*>(&(data[0])));
- lengths_.push_back(len);
- formats_.push_back(BINARY_FMT);
- }
- void PsqlBindArray::add(const bool& value) {
- add(value ? TRUE_STR : FALSE_STR);
- }
- void PsqlBindArray::add(const uint8_t& byte) {
- // We static_cast to an unsigned int, otherwise lexcial_cast may to
- // treat byte as a character, which yields "" for unprintable values
- bindString(boost::lexical_cast<std::string>
- (static_cast<unsigned int>(byte)));
- }
- void PsqlBindArray::add(const isc::asiolink::IOAddress& addr) {
- if (addr.isV4()) {
- bindString(boost::lexical_cast<std::string>
- (static_cast<uint32_t>(addr)));
- } else {
- bindString(addr.toText());
- }
- }
- // eventually this should replace add(std::string)
- void PsqlBindArray::bindString(const std::string& str) {
- bound_strs_.push_back(StringPtr(new std::string(str)));
- PsqlBindArray::add((bound_strs_.back())->c_str());
- }
- std::string PsqlBindArray::toText() const {
- std::ostringstream stream;
- for (int i = 0; i < values_.size(); ++i) {
- stream << i << " : ";
- if (formats_[i] == TEXT_FMT) {
- stream << "\"" << values_[i] << "\"" << std::endl;
- } else {
- const char *data = values_[i];
- if (lengths_[i] == 0) {
- stream << "empty" << std::endl;
- } else {
- stream << "0x";
- for (int i = 0; i < lengths_[i]; ++i) {
- stream << std::setfill('0') << std::setw(2)
- << std::setbase(16)
- << static_cast<unsigned int>(data[i]);
- }
- stream << std::endl;
- }
- }
- }
- return (stream.str());
- }
- std::string
- PgSqlExchange::convertToDatabaseTime(const time_t input_time) {
- struct tm tinfo;
- char buffer[20];
- localtime_r(&input_time, &tinfo);
- strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tinfo);
- return (std::string(buffer));
- }
- std::string
- PgSqlExchange::convertToDatabaseTime(const time_t cltt,
- const uint32_t valid_lifetime) {
- // Calculate expiry time. Store it in the 64-bit value so as we can
- // detect overflows.
- int64_t expire_time_64 = static_cast<int64_t>(cltt)
- + static_cast<int64_t>(valid_lifetime);
- // It has been observed that the PostgreSQL doesn't deal well with the
- // timestamp values beyond the DataSource::MAX_DB_TIME seconds since the
- // beginning of the epoch (around year 2038). The value is often
- // stored in the database but it is invalid when read back (overflow?).
- // Hence, the maximum timestamp value is restricted here.
- if (expire_time_64 > DatabaseConnection::MAX_DB_TIME) {
- isc_throw(isc::BadValue, "Time value is too large: " << expire_time_64);
- }
- return (convertToDatabaseTime(static_cast<time_t>(expire_time_64)));
- }
- time_t
- PgSqlExchange::convertFromDatabaseTime(const std::string& db_time_val) {
- // Convert string time value to time_t
- time_t new_time;
- try {
- new_time = (boost::lexical_cast<time_t>(db_time_val));
- } catch (const std::exception& ex) {
- isc_throw(BadValue, "Database time value is invalid: " << db_time_val);
- }
- return (new_time);
- }
- const char*
- PgSqlExchange::getRawColumnValue(const PgSqlResult& r, const int row,
- const size_t col) const {
- const char* value = PQgetvalue(r, row, col);
- if (!value) {
- isc_throw(DbOperationError, "getRawColumnValue no data for :"
- << getColumnLabel(col) << " row:" << row);
- }
- return (value);
- }
- void
- PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
- const size_t col, bool &value) const {
- const char* data = getRawColumnValue(r, row, col);
- if (!strlen(data) || *data == 'f') {
- value = false;
- } else if (*data == 't') {
- value = true;
- } else {
- isc_throw(DbOperationError, "Invalid boolean data: " << data
- << " for: " << getColumnLabel(col) << " row:" << row
- << " : must be 't' or 'f'");
- }
- }
- void
- PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
- const size_t col, uint8_t &value) const {
- const char* data = getRawColumnValue(r, row, col);
- try {
- // lexically casting as uint8_t doesn't convert from char
- // so we use uint16_t and implicitly convert.
- value = boost::lexical_cast<uint16_t>(data);
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError, "Invalid uint8_t data: " << data
- << " for: " << getColumnLabel(col) << " row:" << row
- << " : " << ex.what());
- }
- }
- void
- PgSqlExchange::convertFromBytea(const PgSqlResult& r, const int row,
- const size_t col, uint8_t* buffer,
- const size_t buffer_size,
- size_t &bytes_converted) const {
- // Returns converted bytes in a dynamically allocated buffer, and
- // sets bytes_converted.
- unsigned char* bytes = PQunescapeBytea((const unsigned char*)
- (getRawColumnValue(r, row, col)),
- &bytes_converted);
- // Unlikely it couldn't allocate it but you never know.
- if (!bytes) {
- isc_throw (DbOperationError, "PQunescapeBytea failed for:"
- << getColumnLabel(col) << " row:" << row);
- }
- // Make sure it's not larger than expected.
- if (bytes_converted > buffer_size) {
- // Free the allocated buffer first!
- PQfreemem(bytes);
- isc_throw (DbOperationError, "Converted data size: "
- << bytes_converted << " is too large for: "
- << getColumnLabel(col) << " row:" << row);
- }
- // Copy from the allocated buffer to caller's buffer the free up
- // the allocated buffer.
- memcpy(buffer, bytes, bytes_converted);
- PQfreemem(bytes);
- }
- std::string
- PgSqlExchange::getColumnLabel(const size_t column) const {
- if (column > column_labels_.size()) {
- std::ostringstream os;
- os << "Unknown column:" << column;
- return (os.str());
- }
- return (column_labels_[column]);
- }
- }; // end of isc::dhcp namespace
- }; // end of isc namespace
|