Parcourir la source

[4277] PgSqlHostWithOptionsExchange, PgSqlOptionExchange now functional

src/lib/dhcpsrv/pgsql_connection.h
    Added OID_TEXT

src/lib/dhcpsrv/pgsql_exchange.cc
    PsqlBindArray::addNull()

    class PgSqlExchange
        - getColumnLabel() - now gets column name from result set
        - getColumnValue variants are now static methods
        - rename column_labels_ to columns_
        - isColumnNull() new method tests if column in row is null
        - dumpRow() - debug method dumps row as text

src/lib/dhcpsrv/pgsql_host_data_source.cc
    PgSqlHostWithOptionsExchange
    PgSqlOptionExchange  now functional

src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc
    TEST_F(PgSqlHostDataSourceTest, addDuplicate4)
    TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations4)
    - Enabled and passing.
Thomas Markwalder il y a 8 ans
Parent
commit
3c01633e93

+ 1 - 0
src/lib/dhcpsrv/pgsql_connection.h

@@ -55,6 +55,7 @@ const size_t OID_BYTEA = 17;
 const size_t OID_INT8 = 20;  // 8 byte int
 const size_t OID_INT4 = 23;  // 4 byte int
 const size_t OID_INT2 = 21;  // 2 byte int
+const size_t OID_TEXT = 25;
 const size_t OID_TIMESTAMP = 1114;
 const size_t OID_VARCHAR = 1043;
 

+ 74 - 11
src/lib/dhcpsrv/pgsql_exchange.cc

@@ -64,6 +64,12 @@ void PsqlBindArray::add(const isc::asiolink::IOAddress& addr) {
     }
 }
 
+void PsqlBindArray::addNull(const int format) {
+    values_.push_back(NULL);
+    lengths_.push_back(0);
+    formats_.push_back(format);
+}
+
 // eventually this should replace add(std::string)
 void PsqlBindArray::bindString(const std::string& str) {
     bound_strs_.push_back(StringPtr(new std::string(str)));
@@ -139,18 +145,30 @@ PgSqlExchange::convertFromDatabaseTime(const std::string& db_time_val) {
 
 const char*
 PgSqlExchange::getRawColumnValue(const PgSqlResult& r, const int row,
-                                 const size_t col) const {
+                                 const size_t col) {
     const char* value = PQgetvalue(r, row, col);
     if (!value) {
         isc_throw(DbOperationError, "getRawColumnValue no data for :"
-                    << getColumnLabel(col) << " row:" << row);
+                    << getColumnLabel(r, col) << " row:" << row);
     }
     return (value);
 }
 
+bool 
+PgSqlExchange::isColumnNull(const PgSqlResult& r, const int row, 
+                            const size_t col) {
+    return (PQgetisnull(r, row, col));
+}
+
 void
 PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
-                              const size_t col, bool &value) const {
+                              const size_t col, std::string& value) {
+    value = getRawColumnValue(r, row, col);
+}
+
+void
+PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
+                              const size_t col, bool &value) {
     const char* data = getRawColumnValue(r, row, col);
     if (!strlen(data) || *data == 'f') {
         value = false;
@@ -158,14 +176,14 @@ PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
         value = true;
     } else {
         isc_throw(DbOperationError, "Invalid boolean data: " << data
-                  << " for: " << getColumnLabel(col) << " row:" << row
+                  << " for: " << getColumnLabel(r, 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 size_t col, uint8_t &value) {
     const char* data = getRawColumnValue(r, row, col);
     try {
         // lexically casting as uint8_t doesn't convert from char
@@ -173,7 +191,7 @@ PgSqlExchange::getColumnValue(const PgSqlResult& r, const int row,
         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
+                  << " for: " << getColumnLabel(r, col) << " row:" << row
                   << " : " << ex.what());
     }
 }
@@ -182,7 +200,7 @@ 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 {
+                                size_t &bytes_converted) {
     // Returns converted bytes in a dynamically allocated buffer, and
     // sets bytes_converted.
     unsigned char* bytes = PQunescapeBytea((const unsigned char*)
@@ -192,7 +210,7 @@ PgSqlExchange::convertFromBytea(const PgSqlResult& r, const int row,
     // Unlikely it couldn't allocate it but you never know.
     if (!bytes) {
         isc_throw (DbOperationError, "PQunescapeBytea failed for:"
-                   << getColumnLabel(col) << " row:" << row);
+                   << getColumnLabel(r, col) << " row:" << row);
     }
 
     // Make sure it's not larger than expected.
@@ -201,7 +219,7 @@ PgSqlExchange::convertFromBytea(const PgSqlResult& r, const int row,
         PQfreemem(bytes);
         isc_throw (DbOperationError, "Converted data size: "
                    << bytes_converted << " is too large for: "
-                   << getColumnLabel(col) << " row:" << row);
+                   << getColumnLabel(r, col) << " row:" << row);
     }
 
     // Copy from the allocated buffer to caller's buffer the free up
@@ -210,15 +228,60 @@ PgSqlExchange::convertFromBytea(const PgSqlResult& r, const int row,
     PQfreemem(bytes);
 }
 
+#if 0
 std::string
 PgSqlExchange::getColumnLabel(const size_t column) const {
-    if (column > column_labels_.size()) {
+    if (column > columns_.size()) {
         std::ostringstream os;
         os << "Unknown column:" << column;
         return (os.str());
     }
 
-    return (column_labels_[column]);
+    return (columns_[column]);
+}
+#endif
+
+std::string
+PgSqlExchange::getColumnLabel(const PgSqlResult& r, const size_t column) {
+    const char* label = PQfname(r, column);
+    if (!label) {
+        std::ostringstream os;
+        os << "Unknown column:" << column;
+        return (os.str());
+    }
+
+    return (label);
+}
+
+std::string 
+PgSqlExchange::dumpRow(const PgSqlResult& r, int row, size_t columns) {
+    std::ostringstream stream;
+    for (int col = 0; col < columns; ++col) {
+        const char* val = getRawColumnValue(r, row, col);
+        std::string name = getColumnLabel(r, col);
+        int format = PQfformat(r, col); 
+
+        stream << col << "   " << name << " : " ;
+        if (format == PsqlBindArray::TEXT_FMT) {
+            stream << "\"" << val << "\"" << std::endl;
+        } else {
+            const char *data = val;
+            int length = PQfsize(r, col);
+            if (length == 0) {
+                stream << "empty" << std::endl;
+            } else {
+                stream << "0x";
+                for (int i = 0; i < length; ++i) {
+                    stream << std::setfill('0') << std::setw(2)
+                           << std::setbase(16)
+                           << static_cast<unsigned int>(data[i]);
+                }
+                stream << std::endl;
+            }
+        }
+    }
+
+    return (stream.str());
 }
 
 }; // end of isc::dhcp namespace

+ 43 - 18
src/lib/dhcpsrv/pgsql_exchange.h

@@ -152,6 +152,12 @@ struct PsqlBindArray {
     /// @param value bool value to add.
     void bindString(const std::string& str);
 
+    /// @brief Adds a NULL value to the bind array
+    ///
+    /// This should be used whenever a the value for a parameter specified
+    /// in the SQL statement should be NULL.
+    void addNull(const int format = PsqlBindArray::TEXT_FMT);
+
     //std::vector<const std::string> getBoundStrs() {
     std::vector<StringPtr> getBoundStrs() {
         return (bound_strs_);
@@ -178,7 +184,7 @@ typedef boost::shared_ptr<PsqlBindArray> PsqlBindArrayPtr;
 class PgSqlExchange {
 public:
     /// @brief Constructor
-    PgSqlExchange(){}
+    PgSqlExchange(const size_t num_columns = 0) : columns_(num_columns) {}
 
     /// @brief Destructor
     virtual ~PgSqlExchange(){}
@@ -239,8 +245,23 @@ public:
     ///
     /// @return a const char* pointer to the column's raw data
     /// @throw  DbOperationError if the value cannot be fetched.
-    const char* getRawColumnValue(const PgSqlResult& r, const int row,
-                                  const size_t col) const;
+    static const char* getRawColumnValue(const PgSqlResult& r, const int row,
+                                         const size_t col);
+
+    /// @todo
+    static std::string getColumnLabel(const PgSqlResult& r, const size_t col);
+
+    /// @brief Fetches text column value as a string
+    ///
+    /// @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 string value
+    ///
+    /// @throw  DbOperationError if the value cannot be fetched or is
+    /// invalid.
+    static void getColumnValue(const PgSqlResult& r, const int row, 
+                               const size_t col, std::string& value);
 
     /// @brief Fetches boolean text ('t' or 'f') as a bool.
     ///
@@ -251,8 +272,8 @@ public:
     ///
     /// @throw  DbOperationError if the value cannot be fetched or is
     /// invalid.
-    void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
-                        bool &value) const;
+    static void getColumnValue(const PgSqlResult& r, const int row, 
+                               const size_t col, bool &value);
 
     /// @brief Fetches an integer text column as a uint8_t.
     ///
@@ -263,8 +284,11 @@ public:
     ///
     /// @throw  DbOperationError if the value cannot be fetched or is
     /// invalid.
-    void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
-                        uint8_t &value) const;
+    static void getColumnValue(const PgSqlResult& r, const int row, 
+                               const size_t col, uint8_t &value);
+
+    static bool isColumnNull(const PgSqlResult& r, const int row, 
+                             const size_t col);
 
     /// @brief Fetches a text column as the given value type
     ///
@@ -279,15 +303,15 @@ public:
     /// @throw  DbOperationError if the value cannot be fetched or is
     /// invalid.
     template<typename T>
-    void getColumnValue(const PgSqlResult& r, const int row, const size_t col,
-                        T& value) const {
+    static void getColumnValue(const PgSqlResult& r, const int row, 
+                               const size_t col, T& value) {
         const char* data = getRawColumnValue(r, row, col);
         try {
             value = boost::lexical_cast<T>(data);
         } catch (const std::exception& ex) {
-            isc_throw(DbOperationError, "Invalid data: " << data
-                      << " for: " << getColumnLabel(col) << " row:" << row
-                      << " : " << ex.what());
+            isc_throw(DbOperationError, "Invalid data:[" << data
+                      << "] for row: " << row << " col: " << col << "," 
+                      << getColumnLabel(r, col) << " : " << ex.what());
         }
     }
 
@@ -307,17 +331,18 @@ public:
     ///
     /// @throw  DbOperationError if the value cannot be fetched or is
     /// invalid.
-    void convertFromBytea(const PgSqlResult& r, const int row, const size_t col,
-                          uint8_t* buffer, const size_t buffer_size,
-                          size_t &bytes_converted) const;
+    static void convertFromBytea(const PgSqlResult& r, const int row, 
+                                 const size_t col, uint8_t* buffer, 
+                                 const size_t buffer_size, 
+                                 size_t &bytes_converted);
+
 
-    /// @brief Returns column label given a column number
-    std::string getColumnLabel(const size_t column) const;
+    static std::string dumpRow(const PgSqlResult& r, int row, size_t columns);
 
 protected:
     /// @brief Stores text labels for columns, currently only used for
     /// logging and errors.
-    std::vector<std::string>column_labels_;
+    std::vector<std::string>columns_;
 };
 
 }; // end of isc::dhcp namespace

Fichier diff supprimé car celui-ci est trop grand
+ 304 - 392
src/lib/dhcpsrv/pgsql_host_data_source.cc


+ 25 - 25
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -280,16 +280,16 @@ public:
         memset(client_id_buffer_, 0, sizeof(client_id_buffer_));
 
         // Set the column names (for error messages)
-        column_labels_.push_back("address");
-        column_labels_.push_back("hwaddr");
-        column_labels_.push_back("client_id");
-        column_labels_.push_back("valid_lifetime");
-        column_labels_.push_back("expire");
-        column_labels_.push_back("subnet_id");
-        column_labels_.push_back("fqdn_fwd");
-        column_labels_.push_back("fqdn_rev");
-        column_labels_.push_back("hostname");
-        column_labels_.push_back("state");
+        columns_.push_back("address");
+        columns_.push_back("hwaddr");
+        columns_.push_back("client_id");
+        columns_.push_back("valid_lifetime");
+        columns_.push_back("expire");
+        columns_.push_back("subnet_id");
+        columns_.push_back("fqdn_fwd");
+        columns_.push_back("fqdn_rev");
+        columns_.push_back("hostname");
+        columns_.push_back("state");
     }
 
     /// @brief Creates the bind array for sending Lease4 data to the database.
@@ -463,19 +463,19 @@ public:
         memset(duid_buffer_, 0, sizeof(duid_buffer_));
 
         // Set the column names (for error messages)
-        column_labels_.push_back("address");
-        column_labels_.push_back("duid");
-        column_labels_.push_back("valid_lifetime");
-        column_labels_.push_back("expire");
-        column_labels_.push_back("subnet_id");
-        column_labels_.push_back("pref_lifetime");
-        column_labels_.push_back("lease_type");
-        column_labels_.push_back("iaid");
-        column_labels_.push_back("prefix_len");
-        column_labels_.push_back("fqdn_fwd");
-        column_labels_.push_back("fqdn_rev");
-        column_labels_.push_back("hostname");
-        column_labels_.push_back("state");
+        columns_.push_back("address");
+        columns_.push_back("duid");
+        columns_.push_back("valid_lifetime");
+        columns_.push_back("expire");
+        columns_.push_back("subnet_id");
+        columns_.push_back("pref_lifetime");
+        columns_.push_back("lease_type");
+        columns_.push_back("iaid");
+        columns_.push_back("prefix_len");
+        columns_.push_back("fqdn_fwd");
+        columns_.push_back("fqdn_rev");
+        columns_.push_back("hostname");
+        columns_.push_back("state");
     }
 
     /// @brief Creates the bind array for sending Lease6 data to the database.
@@ -632,7 +632,7 @@ public:
 
             default:
                 isc_throw(DbOperationError, "Invalid lease type: " << raw_value
-                      << " for: " << getColumnLabel(col) << " row:" << row);
+                      << " for: " << getColumnLabel(r, col) << " row:" << row);
         }
     }
 
@@ -652,7 +652,7 @@ public:
             return (isc::asiolink::IOAddress(data));
         } catch (const std::exception& ex) {
             isc_throw(DbOperationError, "Cannot convert data: " << data
-                      << " for: " << getColumnLabel(col) << " row:" << row
+                      << " for: " << getColumnLabel(r, col) << " row:" << row
                       << " : " << ex.what());
         }
     }

+ 9 - 4
src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc

@@ -60,8 +60,9 @@ public:
 
     /// @brief Destructor
     ///
-    /// Rolls back all pending transactions.  The deletion of myhdsptr_ will close
-    /// the database.  Then reopen it and delete everything created by the test.
+    /// Rolls back all pending transactions.  The deletion of myhdsptr_ will
+    /// close the database.  Then reopen it and delete everything created by
+    /// the test.
     virtual ~PgSqlHostDataSourceTest() {
         hdsptr_->rollback();
         HostDataSourceFactory::destroy();
@@ -73,8 +74,8 @@ public:
     /// Closes the database and re-open it.  Anything committed should be
     /// visible.
     ///
-    /// Parameter is ignored for PostgreSQL backend as the v4 and v6 leases share
-    /// the same database.
+    /// Parameter is ignored for PostgreSQL backend as the v4 and v6 leases
+    /// share the same database.
     void reopen(Universe) {
         HostDataSourceFactory::destroy();
         HostDataSourceFactory::create(validPgSQLConnectionString());
@@ -379,6 +380,7 @@ TEST_F(PgSqlHostDataSourceTest, addDuplicate6WithDUID) {
 TEST_F(PgSqlHostDataSourceTest, addDuplicate6WithHWAddr) {
     testAddDuplicate6WithSameHWAddr();
 }
+#endif
 
 // Test if the duplicate IPv4 host instances can't be inserted. The test logic is as
 // follows: try to add multiple instances of the same host reservation and
@@ -392,6 +394,7 @@ TEST_F(PgSqlHostDataSourceTest, addDuplicate4) {
 TEST_F(PgSqlHostDataSourceTest, optionsReservations4) {
     testOptionsReservations4(false);
 }
+#if 0
 
 // This test verifies that DHCPv6 options can be inserted in a binary format
 /// and retrieved from the PostgreSQL host database.
@@ -404,6 +407,7 @@ TEST_F(PgSqlHostDataSourceTest, optionsReservations6) {
 TEST_F(PgSqlHostDataSourceTest, optionsReservations46) {
     testOptionsReservations46(false);
 }
+#endif
 
 // This test verifies that DHCPv4 options can be inserted in a textual format
 /// and retrieved from the PostgreSQL host database.
@@ -411,6 +415,7 @@ TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations4) {
     testOptionsReservations4(true);
 }
 
+#if 0
 // This test verifies that DHCPv6 options can be inserted in a textual format
 /// and retrieved from the PostgreSQL host database.
 TEST_F(PgSqlHostDataSourceTest, formattedOptionsReservations6) {