if HAVE_PGSQL
libkea_dhcpsrv_la_SOURCES += pgsql_connection.cc pgsql_connection.h
+libkea_dhcpsrv_la_SOURCES += pgsql_exchange.cc pgsql_exchange.h
libkea_dhcpsrv_la_SOURCES += pgsql_lease_mgr.cc pgsql_lease_mgr.h
endif
libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
--- /dev/null
+// 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 bool& value) {
+ add(value ? TRUE_STR : FALSE_STR);
+}
+
+std::string PsqlBindArray::toText() {
+ 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(PGresult*& 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(PGresult*& 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(PGresult*& r, const int row, const size_t col,
+ uint32_t &value) const {
+ const char* data = getRawColumnValue(r, row, col);
+ try {
+ value = boost::lexical_cast<uint32_t>(data);
+ } catch (const std::exception& ex) {
+ isc_throw(DbOperationError, "Invalid uint32_t data: " << data
+ << " for: " << getColumnLabel(col) << " row:" << row
+ << " : " << ex.what());
+ }
+}
+
+void
+PgSqlExchange::getColumnValue(PGresult*& r, const int row, const size_t col,
+ int32_t &value) const {
+ const char* data = getRawColumnValue(r, row, col);
+ try {
+ value = boost::lexical_cast<int32_t>(data);
+ } catch (const std::exception& ex) {
+ isc_throw(DbOperationError, "Invalid int32_t data: " << data
+ << " for: " << getColumnLabel(col) << " row:" << row
+ << " : " << ex.what());
+ }
+}
+
+void
+PgSqlExchange::getColumnValue(PGresult*& 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(PGresult*& 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
--- /dev/null
+// 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/.
+
+#ifndef PGSQL_EXCHANGE_MGR_H
+#define PGSQL_EXCHANGE_MGR_H
+
+#include <dhcpsrv/pgsql_connection.h>
+
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Structure used to bind C++ input values to dynamic SQL parameters
+/// The structure contains three vectors which store the input values,
+/// data lengths, and formats. These vectors are passed directly into the
+/// PostgreSQL execute call.
+///
+/// Note that the data values are stored as pointers. These pointers need to
+/// valid for the duration of the PostgreSQL statement execution. In other
+/// words populating them with pointers to values that go out of scope before
+/// statement is executed is a bad idea.
+struct PsqlBindArray {
+ /// @brief Vector of pointers to the data values.
+ std::vector<const char *> values_;
+ /// @brief Vector of data lengths for each value.
+ std::vector<int> lengths_;
+ /// @brief Vector of "format" for each value. A value of 0 means the
+ /// value is text, 1 means the value is binary.
+ std::vector<int> formats_;
+
+ /// @brief Format value for text data.
+ static const int TEXT_FMT;
+ /// @brief Format value for binary data.
+ static const int BINARY_FMT;
+
+ /// @brief Constant string passed to DB for boolean true values.
+ static const char* TRUE_STR;
+ /// @brief Constant string passed to DB for boolean false values.
+ static const char* FALSE_STR;
+
+ /// @brief Fetches the number of entries in the array.
+ /// @return Returns size_t containing the number of entries.
+ size_t size() {
+ return (values_.size());
+ }
+
+ /// @brief Indicates it the array is empty.
+ /// @return Returns true if there are no entries in the array, false
+ /// otherwise.
+ bool empty() {
+
+ return (values_.empty());
+ }
+
+ /// @brief Adds a char array to bind array based
+ ///
+ /// Adds a TEXT_FMT value to the end of the bind array, using the given
+ /// char* as the data source. Note that value is expected to be NULL
+ /// terminated.
+ ///
+ /// @param value char array containing the null-terminated text to add.
+ void add(const char* value);
+
+ /// @brief Adds an string value to the bind array
+ ///
+ /// Adds a TEXT formatted value to the end of the bind array using the
+ /// given string as the data source.
+ ///
+ /// @param value std::string containing the value to add.
+ void add(const std::string& value);
+
+ /// @brief Adds a binary value to the bind array.
+ ///
+ /// Adds a BINARY_FMT value to the end of the bind array using the
+ /// given vector as the data source.
+ ///
+ /// @param data vector of binary bytes.
+ void add(const std::vector<uint8_t>& data);
+
+ /// @brief Adds a boolean value to the bind array.
+ ///
+ /// Converts the given boolean value to its corresponding to PostgreSQL
+ /// string value and adds it as a TEXT_FMT value to the bind array.
+ ///
+ /// @param value bool value to add.
+ void add(const bool& value);
+
+ /// @brief Dumps the contents of the array to a string.
+ /// @return std::string containing the dump
+ std::string toText();
+};
+
+/// @brief Base class for marshalling data to and from PostgreSQL.
+///
+/// Provides the common functionality to set up binding information between
+/// application objects in the program and their representation in the
+/// database, and for retrieving column values from rows of a result set.
+class PgSqlExchange {
+public:
+ /// @brief Constructor
+ PgSqlExchange(){}
+
+ /// @brief Destructor
+ virtual ~PgSqlExchange(){}
+
+ /// @brief Converts time_t value to a text representation in local time.
+ ///
+ /// @param input_time A time_t value representing time.
+ /// @return std::string containing stringified time.
+ static std::string convertToDatabaseTime(const time_t input_time);
+
+ /// @brief Converts lease expiration time to a text representation in
+ /// local time.
+ ///
+ /// The expiration time is calculated as a sum of the cltt (client last
+ /// transmit time) and the valid lifetime.
+ ///
+ /// The format of the output string is "%Y-%m-%d %H:%M:%S". Database
+ /// table columns using this value should be typed as TIMESTAMP WITH
+ /// TIME ZONE. For such columns PostgreSQL assumes input strings without
+ /// timezones should be treated as in local time and are converted to UTC
+ /// when stored. Likewise, these columns are automatically adjusted
+ /// upon retrieval unless fetched via "extract(epoch from <column>))".
+ ///
+ /// @param cltt Client last transmit time
+ /// @param valid_lifetime Valid lifetime
+ ///
+ /// @return std::string containing the stringified time
+ /// @throw isc::BadValue if the sum of the calculated expiration time is
+ /// greater than the value of @c DataSource::MAX_DB_TIME.
+ static std::string convertToDatabaseTime(const time_t cltt,
+ const uint32_t valid_lifetime);
+
+ /// @brief Converts time stamp from the database to a time_t
+ ///
+ /// @param db_time_val timestamp to be converted. This value
+ /// is expected to be the number of seconds since the epoch
+ /// expressed as base-10 integer string.
+ /// @return Converted timestamp as time_t value.
+ static time_t convertFromDatabaseTime(const std::string& db_time_val);
+
+ /// @brief Gets a pointer to the raw column value in a result set row
+ ///
+ /// Given a result set, row, and column return a const char* pointer to
+ /// the data value in the result set. The pointer is valid as long as
+ /// the result set has not been freed. It may point to text or binary
+ /// data depending on how query was structured. You should not attempt
+ /// to free this pointer.
+ ///
+ /// @param r the result set containing the query results
+ /// @param row the row number within the result set
+ /// @param col the column number within the row
+ ///
+ /// @return a const char* pointer to the column's raw data
+ /// @throw DbOperationError if the value cannot be fetched.
+ const char* getRawColumnValue(PGresult*& r, const int row,
+ const size_t col) const;
+
+ /// @brief Fetches boolean text ('t' or 'f') as a bool.
+ ///
+ /// @param r the result set containing the query results
+ /// @param row the row number within the result set
+ /// @param col the column number within the row
+ /// @param[out] value parameter to receive the converted value
+ ///
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ void getColumnValue(PGresult*& r, const int row, const size_t col,
+ bool &value) const;
+
+ /// @brief Fetches an integer text column as a uint32_t.
+ ///
+ /// @param r the result set containing the query results
+ /// @param row the row number within the result set
+ /// @param col the column number within the row
+ /// @param[out] value parameter to receive the converted value
+ ///
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ void getColumnValue(PGresult*& r, const int row, const size_t col,
+ uint32_t &value) const;
+
+ /// @brief Fetches an integer text column as a int32_t.
+ ///
+ /// @param r the result set containing the query results
+ /// @param row the row number within the result set
+ /// @param col the column number within the row
+ /// @param[out] value parameter to receive the converted value
+ ///
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ void getColumnValue(PGresult*& r, const int row, const size_t col,
+ int32_t &value) const;
+
+ /// @brief Fetches an integer text column as a uint8_t.
+ ///
+ /// @param r the result set containing the query results
+ /// @param row the row number within the result set
+ /// @param col the column number within the row
+ /// @param[out] value parameter to receive the converted value
+ ///
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ void getColumnValue(PGresult*& r, const int row, const size_t col,
+ uint8_t &value) const;
+
+ /// @brief Converts a column in a row in a result set to a binary bytes
+ ///
+ /// Method is used to convert columns stored as BYTEA into a buffer of
+ /// binary bytes, (uint8_t). It uses PQunescapeBytea to do the conversion.
+ ///
+ /// @param r the result set containing the query results
+ /// @param row the row number within the result set
+ /// @param col the column number within the row
+ /// @param[out] buffer pre-allocated buffer to which the converted bytes
+ /// will be stored.
+ /// @param buffer_size size of the output buffer
+ /// @param[out] bytes_converted number of bytes converted
+ /// value
+ ///
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ void convertFromBytea(PGresult*& r, const int row, const size_t col,
+ uint8_t* buffer, const size_t buffer_size,
+ size_t &bytes_converted) const;
+
+ /// @brief Returns column label given a column number
+ std::string getColumnLabel(const size_t column) const;
+
+protected:
+ /// @brief Stores text labels for columns, currently only used for
+ /// logging and errors.
+ std::vector<std::string>column_labels_;
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // PGSQL_EXCHANGE_MGR_H
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 bool& value) {
- add(value ? TRUE_STR : FALSE_STR);
-}
-
-std::string PsqlBindArray::toText() {
- 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 << setfill('0') << setw(2) << setbase(16)
- << static_cast<unsigned int>(data[i]);
- }
- stream << std::endl;
- }
- }
- }
-
- return (stream.str());
-}
-
/// @brief Base class for marshalling leases to and from PostgreSQL.
///
/// Provides the common functionality to set up binding information between
/// lease objects in the program and their database representation in the
/// database.
-class PgSqlLeaseExchange {
+class PgSqlLeaseExchange : public PgSqlExchange {
public:
-
PgSqlLeaseExchange()
: addr_str_(""), valid_lifetime_(0), valid_lft_str_(""),
expire_(0), expire_str_(""), subnet_id_(0), subnet_id_str_(""),
virtual ~PgSqlLeaseExchange(){}
- /// @brief Converts time_t value to a text representation in local time.
- ///
- /// @param input_time A time_t value representing time.
- /// @return std::string containing stringified time.
- static std::string
- 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));
- }
-
- /// @brief Converts lease expiration time to a text representation in
- /// local time.
- ///
- /// The expiration time is calculated as a sum of the cltt (client last
- /// transmit time) and the valid lifetime.
- ///
- /// The format of the output string is "%Y-%m-%d %H:%M:%S". Database
- /// table columns using this value should be typed as TIMESTAMP WITH
- /// TIME ZONE. For such columns PostgreSQL assumes input strings without
- /// timezones should be treated as in local time and are converted to UTC
- /// when stored. Likewise, these columns are automatically adjusted
- /// upon retrieval unless fetched via "extract(epoch from <column>))".
- ///
- /// @param cltt Client last transmit time
- /// @param valid_lifetime Valid lifetime
- ///
- /// @return std::string containing the stringified time
- /// @throw isc::BadValue if the sum of the calculated expiration time is
- /// greater than the value of @c DataSource::MAX_DB_TIME.
- static std::string
- 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)));
- }
-
- /// @brief Converts time stamp from the database to a time_t
- ///
- /// @param db_time_val timestamp to be converted. This value
- /// is expected to be the number of seconds since the epoch
- /// expressed as base-10 integer string.
- /// @return Converted timestamp as time_t value.
- static time_t convertFromDatabaseTime(const std::string& db_time_val) {
- // Convert string time value to time_t
- try {
- return (boost::lexical_cast<time_t>(db_time_val));
- } catch (const std::exception& ex) {
- isc_throw(BadValue, "Database time value is invalid: "
- << db_time_val);
- }
- }
-
- /// @brief Gets a pointer to the raw column value in a result set row
- ///
- /// Given a result set, row, and column return a const char* pointer to
- /// the data value in the result set. The pointer is valid as long as
- /// the result set has not been freed. It may point to text or binary
- /// data depending on how query was structured. You should not attempt
- /// to free this pointer.
- ///
- /// @param r the result set containing the query results
- /// @param row the row number within the result set
- /// @param col the column number within the row
- ///
- /// @return a const char* pointer to the column's raw data
- /// @throw DbOperationError if the value cannot be fetched.
- const char* getRawColumnValue(PGresult*& 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);
- }
-
- /// @brief Fetches boolean text ('t' or 'f') as a bool.
- ///
- /// @param r the result set containing the query results
- /// @param row the row number within the result set
- /// @param col the column number within the row
- /// @param[out] value parameter to receive the converted value
- ///
- /// @throw DbOperationError if the value cannot be fetched or is
- /// invalid.
- void getColumnValue(PGresult*& 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'");
- }
- }
-
- /// @brief Fetches an integer text column as a uint32_t.
- ///
- /// @param r the result set containing the query results
- /// @param row the row number within the result set
- /// @param col the column number within the row
- /// @param[out] value parameter to receive the converted value
- ///
- /// @throw DbOperationError if the value cannot be fetched or is
- /// invalid.
- void getColumnValue(PGresult*& r, const int row, const size_t col,
- uint32_t &value) const {
- const char* data = getRawColumnValue(r, row, col);
- try {
- value = boost::lexical_cast<uint32_t>(data);
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError, "Invalid uint32_t data: " << data
- << " for: " << getColumnLabel(col) << " row:" << row
- << " : " << ex.what());
- }
- }
-
- /// @brief Fetches an integer text column as a int32_t.
- ///
- /// @param r the result set containing the query results
- /// @param row the row number within the result set
- /// @param col the column number within the row
- /// @param[out] value parameter to receive the converted value
- ///
- /// @throw DbOperationError if the value cannot be fetched or is
- /// invalid.
- void getColumnValue(PGresult*& r, const int row, const size_t col,
- int32_t &value) const {
- const char* data = getRawColumnValue(r, row, col);
- try {
- value = boost::lexical_cast<int32_t>(data);
- } catch (const std::exception& ex) {
- isc_throw(DbOperationError, "Invalid int32_t data: " << data
- << " for: " << getColumnLabel(col) << " row:" << row
- << " : " << ex.what());
- }
- }
-
- /// @brief Fetches an integer text column as a uint8_t.
- ///
- /// @param r the result set containing the query results
- /// @param row the row number within the result set
- /// @param col the column number within the row
- /// @param[out] value parameter to receive the converted value
- ///
- /// @throw DbOperationError if the value cannot be fetched or is
- /// invalid.
- void getColumnValue(PGresult*& 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());
- }
- }
-
- /// @brief Fetches an integer text column as a Lease6::Type
- ///
- /// @param r the result set containing the query results
- /// @param row the row number within the result set
- /// @param col the column number within the row
- /// @param[out] value parameter to receive the converted value
- ///
- /// @throw DbOperationError if the value cannot be fetched or is
- /// invalid.
- void getColumnValue(PGresult*& r, const int row, const size_t col,
- Lease6::Type& value) const {
- uint32_t raw_value = 0;
- getColumnValue(r, row , col, raw_value);
- switch (raw_value) {
- case Lease6::TYPE_NA:
- value = Lease6::TYPE_NA;
- break;
-
- case Lease6::TYPE_TA:
- value = Lease6::TYPE_TA;
- break;
-
- case Lease6::TYPE_PD:
- value = Lease6::TYPE_PD;
- break;
-
- default:
- isc_throw(DbOperationError, "Invalid lease type: " << raw_value
- << " for: " << getColumnLabel(col) << " row:" << row);
- }
- }
-
- /// @brief Converts a column in a row in a result set to a binary bytes
- ///
- /// Method is used to convert columns stored as BYTEA into a buffer of
- /// binary bytes, (uint8_t). It uses PQunescapeBytea to do the conversion.
- ///
- /// @param r the result set containing the query results
- /// @param row the row number within the result set
- /// @param col the column number within the row
- /// @param[out] buffer pre-allocated buffer to which the converted bytes
- /// will be stored.
- /// @param buffer_size size of the output buffer
- /// @param[out] bytes_converted number of bytes converted
- /// value
- ///
- /// @throw DbOperationError if the value cannot be fetched or is
- /// invalid.
- void convertFromBytea(PGresult*& 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);
- }
-
- /// @brief Returns column label given a column number
- std::string getColumnLabel(const size_t column) const {
- if (column > column_labels_.size()) {
- ostringstream os;
- os << "Unknown column:" << column;
- return (os.str());
- }
-
- return (column_labels_[column]);
- }
-
protected:
- /// @brief Stores text labels for columns, currently only used for
- /// logging and errors.
- std::vector<std::string>column_labels_;
-
/// @brief Common Instance members used for binding and conversion
//@{
std::string addr_str_;
getColumnValue(r, row , PREF_LIFETIME_COL, pref_lifetime_);
- getColumnValue(r, row, LEASE_TYPE_COL, lease_type_);
+ getLeaseTypeColumnValue(r, row, LEASE_TYPE_COL, lease_type_);
getColumnValue(r, row , IAID_COL, iaid_u_.ival_);
}
}
+ /// @brief Fetches an integer text column as a Lease6::Type
+ ///
+ /// @param r the result set containing the query results
+ /// @param row the row number within the result set
+ /// @param col the column number within the row
+ /// @param[out] value parameter to receive the converted value
+ ///
+ /// Note we depart from overloading getColumnValue to avoid ambiguity
+ /// with base class methods for integers.
+ ///
+ /// @throw DbOperationError if the value cannot be fetched or is
+ /// invalid.
+ void getLeaseTypeColumnValue(PGresult*& r, const int row, const size_t col,
+ Lease6::Type& value) const {
+ uint32_t raw_value = 0;
+ getColumnValue(r, row , col, raw_value);
+ switch (raw_value) {
+ case Lease6::TYPE_NA:
+ case Lease6::TYPE_TA:
+ case Lease6::TYPE_PD:
+ value = static_cast<Lease6::Type>(raw_value);
+ break;
+
+ default:
+ isc_throw(DbOperationError, "Invalid lease type: " << raw_value
+ << " for: " << getColumnLabel(col) << " row:" << row);
+ }
+ }
+
/// @brief Converts a column in a row in a result set into IPv6 address.
///
/// @param r the result set containing the query results
: LeaseMgr(), exchange4_(new PgSqlLease4Exchange()),
exchange6_(new PgSqlLease6Exchange()), conn_(parameters) {
conn_.openDatabase();
- prepareStatements();
+ int i = 0;
+ for( ; tagged_statements[i].text != NULL ; ++i) {
+ conn_.prepareStatement(tagged_statements[i]);
+ }
+
+ // Just in case somebody foo-barred things
+ if (i != NUM_STATEMENTS) {
+ isc_throw(DbOpenError, "Number of statements prepared: " << i
+ << " does not match expected count:" << NUM_STATEMENTS);
+ }
}
PgSqlLeaseMgr::~PgSqlLeaseMgr() {
return (tmp.str());
}
-void
-PgSqlLeaseMgr::prepareStatements() {
- for(int i = 0; tagged_statements[i].text != NULL; ++ i) {
- conn_.prepareStatement(tagged_statements[i]);
- }
-}
-
bool
PgSqlLeaseMgr::addLeaseCommon(StatementIndex stindex,
PsqlBindArray& bind_array) {
#include <dhcp/hwaddr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/pgsql_connection.h>
+#include <dhcpsrv/pgsql_exchange.h>
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
namespace isc {
namespace dhcp {
-/// @brief Structure used to bind C++ input values to dynamic SQL parameters
-/// The structure contains three vectors which store the input values,
-/// data lengths, and formats. These vectors are passed directly into the
-/// PostgreSQL execute call.
-///
-/// Note that the data values are stored as pointers. These pointers need to
-/// valid for the duration of the PostgreSQL statement execution. In other
-/// words populating them with pointers to values that go out of scope before
-/// statement is executed is a bad idea.
-struct PsqlBindArray {
- /// @brief Vector of pointers to the data values.
- std::vector<const char *> values_;
- /// @brief Vector of data lengths for each value.
- std::vector<int> lengths_;
- /// @brief Vector of "format" for each value. A value of 0 means the
- /// value is text, 1 means the value is binary.
- std::vector<int> formats_;
-
- /// @brief Format value for text data.
- static const int TEXT_FMT;
- /// @brief Format value for binary data.
- static const int BINARY_FMT;
-
- /// @brief Constant string passed to DB for boolean true values.
- static const char* TRUE_STR;
- /// @brief Constant string passed to DB for boolean false values.
- static const char* FALSE_STR;
-
- /// @brief Fetches the number of entries in the array.
- /// @return Returns size_t containing the number of entries.
- size_t size() {
- return (values_.size());
- }
-
- /// @brief Indicates it the array is empty.
- /// @return Returns true if there are no entries in the array, false
- /// otherwise.
- bool empty() {
-
- return (values_.empty());
- }
-
- /// @brief Adds a char array to bind array based
- ///
- /// Adds a TEXT_FMT value to the end of the bind array, using the given
- /// char* as the data source. Note that value is expected to be NULL
- /// terminated.
- ///
- /// @param value char array containing the null-terminated text to add.
- void add(const char* value);
-
- /// @brief Adds an string value to the bind array
- ///
- /// Adds a TEXT formatted value to the end of the bind array using the
- /// given string as the data source.
- ///
- /// @param value std::string containing the value to add.
- void add(const std::string& value);
-
- /// @brief Adds a binary value to the bind array.
- ///
- /// Adds a BINARY_FMT value to the end of the bind array using the
- /// given vector as the data source.
- ///
- /// @param data vector of binary bytes.
- void add(const std::vector<uint8_t>& data);
-
- /// @brief Adds a boolean value to the bind array.
- ///
- /// Converts the given boolean value to its corresponding to PostgreSQL
- /// string value and adds it as a TEXT_FMT value to the bind array.
- ///
- /// @param value bool value to add.
- void add(const bool& value);
-
- /// @brief Dumps the contents of the array to a string.
- /// @return std::string containing the dump
- std::string toText();
-};
-
// Forward definitions (needed for shared_ptr definitions)
// See pgsql_lease_mgr.cc file for actual class definitions
class PgSqlLease4Exchange;
private:
- /// @brief Prepare statements
- ///
- /// Creates the prepared statements for all of the SQL statements used
- /// by the PostgreSQL backend.
- ///
- /// @throw isc::dhcp::DbOperationError An operation on the open database has
- /// failed.
- /// @throw isc::InvalidParameter 'index' is not valid for the vector. This
- /// represents an internal error within the code.
- void prepareStatements();
-
- /// @brief Open Database
- ///
- /// Opens the database using the information supplied in the parameters
- /// passed to the constructor.
- ///
- /// @throw NoDatabaseName Mandatory database name not given
- /// @throw DbOpenError Error opening the database
- void openDatabase();
-
/// @brief Add Lease Common Code
///
/// This method performs the common actions for both flavours (V4 and V6)