mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 14:05:33 +00:00
[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.
This commit is contained in:
@@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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) {
|
||||
|
Reference in New Issue
Block a user