Commit 3c01633e authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[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.
parent 8047f978
......@@ -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;
......
......@@ -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
......
......@@ -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
......
This diff is collapsed.
......@@ -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());
}
}
......
......@@ -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) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment