Browse Source

[4276] Created new base class, PgSqlExchange

src/lib/dhcpsrv/Makefile.am
    Added pgsql_exchange.cc and pgsql_exchange.h

src/lib/dhcpsrv/pgsql_exchange.h
src/lib/dhcpsrv/pgsql_exchange.cc
    New files, containng new base class PgSqlExchange from
    which was distilled from PgSqlLeaseExchange

src/lib/dhcpsrv/pgsql_lease_mgr.cc
    Refactored exchange classes to use new base class
    Moved PsqlBindArray into pgsql_exchange.*
Thomas Markwalder 9 years ago
parent
commit
09e4931b63

+ 1 - 0
src/lib/dhcpsrv/Makefile.am

@@ -134,6 +134,7 @@ libkea_dhcpsrv_la_SOURCES += ncr_generator.cc ncr_generator.h
 
 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

+ 222 - 0
src/lib/dhcpsrv/pgsql_exchange.cc

@@ -0,0 +1,222 @@
+// 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

+ 243 - 0
src/lib/dhcpsrv/pgsql_exchange.h

@@ -0,0 +1,243 @@
+// 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

+ 41 - 337
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -208,66 +208,14 @@ PgSqlTaggedStatement tagged_statements[] = {
 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_(""),
@@ -277,282 +225,7 @@ public:
 
     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_;
@@ -905,7 +578,7 @@ public:
 
             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_);
 
@@ -933,6 +606,35 @@ public:
         }
     }
 
+    /// @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
@@ -993,7 +695,16 @@ PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
     : 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() {
@@ -1008,13 +719,6 @@ PgSqlLeaseMgr::getDBVersion() {
     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) {

+ 1 - 100
src/lib/dhcpsrv/pgsql_lease_mgr.h

@@ -10,6 +10,7 @@
 #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>
@@ -19,86 +20,6 @@
 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;
@@ -469,26 +390,6 @@ public:
 
 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)